hobots 0.3.1 → 1.0.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/LICENSE +21 -0
- package/README.md +346 -0
- package/dist/chunk-FW32OR7V.js +75 -0
- package/dist/chunk-FW32OR7V.js.map +1 -0
- package/dist/index.cjs +764 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.ts +187 -0
- package/dist/index.js +691 -0
- package/dist/index.js.map +1 -0
- package/dist/manager/protocol.cjs +19 -0
- package/dist/manager/protocol.cjs.map +1 -0
- package/dist/manager/protocol.d.cts +72 -0
- package/dist/manager/protocol.d.ts +72 -0
- package/dist/manager/protocol.js +1 -0
- package/dist/manager/protocol.js.map +1 -0
- package/dist/manager.cjs +519 -0
- package/dist/manager.cjs.map +1 -0
- package/dist/manager.d.cts +118 -0
- package/dist/manager.d.ts +118 -0
- package/dist/manager.js +464 -0
- package/dist/manager.js.map +1 -0
- package/dist/protocol.cjs +19 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.cts +126 -0
- package/dist/protocol.d.ts +126 -0
- package/dist/protocol.js +1 -0
- package/dist/protocol.js.map +1 -0
- package/dist/utils-LReLqjzN.d.cts +7 -0
- package/dist/utils-LReLqjzN.d.ts +7 -0
- package/docs/README.md +39 -0
- package/docs/api-reference.md +140 -0
- package/docs/getting-started.md +88 -0
- package/docs/manager.md +90 -0
- package/docs/monitor.md +136 -0
- package/docs/protocol.md +87 -0
- package/package.json +50 -2
- package/auth/index.d.ts +0 -10
- package/csv/index.d.ts +0 -9
- package/datasets/index.d.ts +0 -2
- package/db/drizzle.config.d.ts +0 -8
- package/db/index.d.ts +0 -9
- package/db/migrate.d.ts +0 -3
- package/db/schema.d.ts +0 -215
- package/db/storages/base.d.ts +0 -23
- package/db/storages/index.d.ts +0 -3
- package/db/storages/job.d.ts +0 -233
- package/exceptions/break.d.ts +0 -6
- package/exceptions/ignore.d.ts +0 -6
- package/exceptions/index.d.ts +0 -4
- package/exceptions/warning.d.ts +0 -6
- package/foreach/foreach.d.ts +0 -7
- package/foreach/index.d.ts +0 -5
- package/foreach/item.d.ts +0 -13
- package/hobots/samples/bot.d.ts +0 -2
- package/hobots/samples/index.d.ts +0 -2
- package/hobots/src/auth/index.d.ts +0 -10
- package/hobots/src/csv/index.d.ts +0 -9
- package/hobots/src/datasets/index.d.ts +0 -2
- package/hobots/src/db/drizzle.config.d.ts +0 -8
- package/hobots/src/db/index.d.ts +0 -9
- package/hobots/src/db/migrate.d.ts +0 -3
- package/hobots/src/db/schema.d.ts +0 -215
- package/hobots/src/db/storages/base.d.ts +0 -23
- package/hobots/src/db/storages/index.d.ts +0 -3
- package/hobots/src/db/storages/job.d.ts +0 -233
- package/hobots/src/exceptions/break.d.ts +0 -6
- package/hobots/src/exceptions/ignore.d.ts +0 -6
- package/hobots/src/exceptions/index.d.ts +0 -4
- package/hobots/src/exceptions/warning.d.ts +0 -6
- package/hobots/src/foreach/foreach.d.ts +0 -7
- package/hobots/src/foreach/index.d.ts +0 -5
- package/hobots/src/foreach/item.d.ts +0 -13
- package/hobots/src/index.d.ts +0 -9
- package/hobots/src/reports/data-report.d.ts +0 -9
- package/hobots/src/reports/index.d.ts +0 -3
- package/hobots/src/reports/job-report.d.ts +0 -22
- package/hobots/src/setup.d.ts +0 -20
- package/hobots/src/sheets/index.d.ts +0 -13
- package/hobots/src/types/hobots-types.d.ts +0 -20
- package/hobots/src/utils/date.d.ts +0 -117
- package/hobots/src/utils/dist.d.ts +0 -8
- package/hobots/src/utils/garbage.d.ts +0 -2
- package/hobots/src/utils/index.d.ts +0 -10
- package/hobots/src/utils/logger.d.ts +0 -21
- package/hobots/src/utils/manifest.d.ts +0 -2
- package/hobots/src/utils/path.d.ts +0 -13
- package/hobots/src/utils/publish.d.ts +0 -2
- package/hobots/src/utils/retry.d.ts +0 -5
- package/hobots/src/utils/text.d.ts +0 -74
- package/hobots/src/utils/timer.d.ts +0 -3
- package/index.d.ts +0 -9
- package/index.js +0 -131866
- package/index.js.map +0 -1
- package/reports/data-report.d.ts +0 -9
- package/reports/index.d.ts +0 -3
- package/reports/job-report.d.ts +0 -22
- package/setup.d.ts +0 -20
- package/sheets/index.d.ts +0 -13
- package/types/hobots-types.d.ts +0 -20
- package/utils/date.d.ts +0 -117
- package/utils/dist.d.ts +0 -8
- package/utils/garbage.d.ts +0 -2
- package/utils/index.d.ts +0 -10
- package/utils/logger.d.ts +0 -21
- package/utils/manifest.d.ts +0 -2
- package/utils/path.d.ts +0 -13
- package/utils/publish.d.ts +0 -2
- package/utils/retry.d.ts +0 -5
- package/utils/src/path.d.ts +0 -14
- package/utils/text.d.ts +0 -74
- package/utils/timer.d.ts +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/manager/api.ts","../src/manager/context.ts","../src/manager/dsn.ts","../src/manager/agent.ts","../src/manager/core.ts"],"sourcesContent":["import type { ManagerPollRequest, ManagerPollResponse, ManagerRunUpdateRequest } from './protocol';\nimport { sleep, type Logger } from '../utils';\n\nconst RETRY_DELAYS_MS = [1_000, 4_000, 16_000];\n\nexport class ManagerApiError extends Error {\n constructor(\n message: string,\n readonly status: number\n ) {\n super(message);\n this.name = 'ManagerApiError';\n }\n}\n\nexport interface ManagerApiOptions {\n baseUrl: string;\n key: string;\n logger: Logger;\n}\n\n/** Cliente HTTP do agente — bearer auth, retry com backoff nos updates. */\nexport class ManagerApi {\n private readonly baseUrl: string;\n private readonly key: string;\n private readonly logger: Logger;\n\n constructor(options: ManagerApiOptions) {\n this.baseUrl = options.baseUrl;\n this.key = options.key;\n this.logger = options.logger;\n }\n\n private headers(): Record<string, string> {\n return {\n 'content-type': 'application/json',\n authorization: `Bearer ${this.key}`,\n };\n }\n\n /** Um request só — o loop de polling é quem decide o backoff entre tentativas. */\n async poll(request: ManagerPollRequest, signal?: AbortSignal): Promise<ManagerPollResponse> {\n const response = await fetch(`${this.baseUrl}/poll`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify(request),\n signal,\n });\n if (!response.ok) {\n throw new ManagerApiError(`poll falhou (HTTP ${response.status})`, response.status);\n }\n return (await response.json()) as ManagerPollResponse;\n }\n\n /**\n * PATCH de status/logs com retry em falha de rede/5xx/429.\n * 4xx é definitivo e vira ManagerApiError imediato (409 = run cancelada/transição inválida).\n */\n async updateRun(runId: string, update: ManagerRunUpdateRequest): Promise<void> {\n for (let attempt = 0; ; attempt++) {\n let retriable: unknown;\n try {\n const response = await fetch(`${this.baseUrl}/runs/${encodeURIComponent(runId)}`, {\n method: 'PATCH',\n headers: this.headers(),\n body: JSON.stringify(update),\n });\n if (response.ok) return;\n if (response.status >= 400 && response.status < 500 && response.status !== 429) {\n throw new ManagerApiError(`update rejeitado (HTTP ${response.status})`, response.status);\n }\n retriable = new Error(`HTTP ${response.status}`);\n } catch (error) {\n if (error instanceof ManagerApiError) throw error;\n retriable = error;\n }\n\n const delay = RETRY_DELAYS_MS[attempt];\n if (delay === undefined) {\n throw new Error(`update da run ${runId} descartado após retries: ${String(retriable)}`);\n }\n this.logger.warn(`falha no update da run ${runId} (${String(retriable)}), retry em ${delay}ms`);\n await sleep(delay);\n }\n }\n}\n","import type { ManagerRunError, ManagerRunUpdateRequest, RunLogEntry, RunLogLevel } from './protocol';\nimport type { RunContext } from './types';\nimport { ManagerApi, ManagerApiError } from './api';\nimport { nowISO, unrefTimer, type Logger } from '../utils';\n\nconst DEFAULT_LOG_FLUSH_INTERVAL_MS = 2_000;\nconst MAX_LOG_MESSAGE_CHARS = 10_000;\nconst MAX_LOGS_PER_RUN = 500;\nconst MAX_LOGS_PER_PATCH = 100;\nconst FLUSH_AT_BUFFERED = 20;\nconst MAX_RESULT_CHARS = 100_000;\n\n/**\n * Reporter de uma run: bufferiza logs (flush incremental com throttle) e encadeia\n * todos os PATCHes numa fila única — a ordem running → logs → terminal é garantida.\n * Um 409/404 mata o reporter (run cancelada no app): o handler não pode ser morto,\n * mas todo reporte posterior é suprimido e ctx.signal dispara para abort cooperativo.\n */\nexport class RunReporter {\n readonly context: RunContext;\n\n private buffer: RunLogEntry[] = [];\n private loggedCount = 0;\n private droppedCount = 0;\n private dead = false;\n private chain: Promise<void> = Promise.resolve();\n private timer: ReturnType<typeof setTimeout> | undefined;\n private readonly abortController = new AbortController();\n private readonly flushIntervalMs: number;\n\n constructor(\n private readonly api: ManagerApi,\n private readonly runId: string,\n task: string,\n private readonly logger: Logger,\n flushIntervalMs?: number\n ) {\n this.flushIntervalMs = flushIntervalMs ?? DEFAULT_LOG_FLUSH_INTERVAL_MS;\n this.context = {\n runId,\n task,\n log: (level, message) => this.log(level, message),\n signal: this.abortController.signal,\n };\n }\n\n get isDead(): boolean {\n return this.dead;\n }\n\n /** Aborta o ctx.signal sem matar o reporter (timeout/shutdown ainda reportam o terminal). */\n abort(): void {\n if (!this.abortController.signal.aborted) this.abortController.abort();\n }\n\n private log(level: RunLogLevel, message: string): void {\n if (this.dead) return;\n if (this.loggedCount >= MAX_LOGS_PER_RUN) {\n this.droppedCount++;\n return;\n }\n this.loggedCount++;\n const truncated =\n message.length > MAX_LOG_MESSAGE_CHARS ? `${message.slice(0, MAX_LOG_MESSAGE_CHARS)}… [truncado]` : message;\n this.buffer.push({ ts: nowISO(), level, message: truncated });\n\n if (this.buffer.length >= FLUSH_AT_BUFFERED) {\n this.flushLogs();\n } else {\n this.scheduleFlush();\n }\n }\n\n private scheduleFlush(): void {\n if (this.timer) return;\n this.timer = setTimeout(() => {\n this.timer = undefined;\n this.flushLogs();\n }, this.flushIntervalMs);\n unrefTimer(this.timer);\n }\n\n private clearFlushTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private takeLogs(): RunLogEntry[] {\n const logs = this.buffer.splice(0, this.buffer.length);\n if (this.droppedCount > 0) {\n logs.push({\n ts: nowISO(),\n level: 'warning',\n message: `${this.droppedCount} logs descartados (limite de ${MAX_LOGS_PER_RUN} por execução)`,\n });\n this.droppedCount = 0;\n }\n return logs;\n }\n\n /** Flush incremental: envia o buffer como status 'running' (em chunks). */\n private flushLogs(): void {\n this.clearFlushTimer();\n if (this.dead || this.buffer.length === 0) return;\n const logs = this.buffer.splice(0, this.buffer.length);\n for (let i = 0; i < logs.length; i += MAX_LOGS_PER_PATCH) {\n this.enqueue({ status: 'running', logs: logs.slice(i, i + MAX_LOGS_PER_PATCH) });\n }\n }\n\n /** Primeiro PATCH (claimed → running). Retorna false se a run já morreu (cancelada). */\n async reportRunning(): Promise<boolean> {\n this.enqueue({ status: 'running' });\n await this.chain;\n return !this.dead;\n }\n\n reportSuccess(result: unknown): void {\n this.clearFlushTimer();\n this.enqueue({ status: 'succeeded', result: serializeResult(result, this.logger), logs: this.takeLogs() });\n }\n\n reportFailure(error: ManagerRunError): void {\n this.clearFlushTimer();\n this.enqueue({ status: 'failed', error, logs: this.takeLogs() });\n }\n\n /** Aguarda a fila de PATCHes esvaziar (chame depois do terminal). */\n finalize(): Promise<void> {\n return this.chain;\n }\n\n private enqueue(update: ManagerRunUpdateRequest): void {\n if (this.dead) return;\n this.chain = this.chain.then(async () => {\n if (this.dead) return;\n const payload = update.logs && update.logs.length === 0 ? { ...update, logs: undefined } : update;\n try {\n await this.api.updateRun(this.runId, payload);\n } catch (error) {\n if (error instanceof ManagerApiError && (error.status === 409 || error.status === 404)) {\n this.dead = true;\n this.abort();\n this.buffer = [];\n this.logger.warn(`run ${this.runId} cancelada/removida no servidor — reporte suprimido`);\n return;\n }\n this.logger.error(`update da run ${this.runId} perdido:`, error);\n }\n });\n }\n}\n\n/** Serializa o result com cap de tamanho — um result gigante não pode falhar uma run bem-sucedida. */\nfunction serializeResult(result: unknown, logger: Logger): unknown {\n if (result === undefined) return undefined;\n let json: string;\n try {\n json = JSON.stringify(result);\n } catch {\n logger.warn('result não serializável (circular/BigInt?) — descartado');\n return { __truncated: true, message: 'resultado não serializável e foi descartado pelo SDK' };\n }\n if (json === undefined) return undefined;\n if (json.length > MAX_RESULT_CHARS) {\n logger.warn(`result excede ${MAX_RESULT_CHARS} chars — descartado`);\n return { __truncated: true, message: 'resultado excede 100KB e foi descartado pelo SDK' };\n }\n return result;\n}\n","import { parseDsnUrl } from '../dsn';\n\nexport interface ParsedManagerDsn {\n /** Chave secreta do agente (enviada como Authorization: Bearer, nunca na URL) */\n key: string;\n projectSlug: string;\n origin: string;\n /** Base derivada: <origin>/api/manager */\n baseUrl: string;\n}\n\n/** Formato: https://<managerKey>@host[:porta]/<projectSlug> */\nexport function parseManagerDsn(dsn: string): ParsedManagerDsn {\n const { key, path, origin } = parseDsnUrl(dsn, '[hobots-manager]');\n return {\n key,\n projectSlug: path,\n origin,\n baseUrl: `${origin}/api/manager`,\n };\n}\n","import type { ManagerRunDescriptor, ManagerRunError } from './protocol';\nimport type { ManagerInitOptions, ManagerTaskHandler } from './types';\nimport { ManagerApi, ManagerApiError } from './api';\nimport { RunReporter } from './context';\nimport { parseManagerDsn } from './dsn';\nimport { isNode, makeLogger, sleep, SDK_VERSION, type Logger } from '../utils';\n\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst MIN_POLL_INTERVAL_MS = 1_000;\nconst MAX_CONCURRENCY = 5;\nconst POLL_RETRY_DELAYS_MS = [1_000, 4_000, 16_000];\nconst AUTH_FAILURE_INTERVAL_MS = 30_000;\n\ntype AgentState = 'idle' | 'started' | 'stopping';\n\nfunction defaultAgentName(): string | undefined {\n if (!isNode()) return undefined;\n return process.env.HOSTNAME || process.env.COMPUTERNAME || undefined;\n}\n\n/**\n * Loop de polling do Manager: busca runs pendentes, executa os handlers registrados\n * e reporta status/logs/result. Ao contrário do Monitor, o timer de polling NÃO é\n * unref'd — um agente iniciado mantém o processo vivo até stop().\n */\nexport class ManagerAgent {\n readonly logger: Logger;\n\n private readonly api: ManagerApi;\n private readonly pollIntervalMs: number;\n private readonly concurrency: number;\n private readonly agentName?: string;\n private readonly taskTimeoutMs?: number;\n private readonly logFlushIntervalMs?: number;\n\n private state: AgentState = 'idle';\n private timer: ReturnType<typeof setTimeout> | undefined;\n private pollAbort: AbortController | undefined;\n private consecutiveFailures = 0;\n private consecutiveAuthFailures = 0;\n private readonly activeRuns = new Set<Promise<void>>();\n private readonly reporters = new Set<RunReporter>();\n\n constructor(\n private readonly options: ManagerInitOptions,\n private readonly registry: Map<string, ManagerTaskHandler>\n ) {\n this.logger = makeLogger(options.debug ?? false, '[hobots-manager]');\n const dsn = parseManagerDsn(options.dsn);\n this.api = new ManagerApi({ baseUrl: dsn.baseUrl, key: dsn.key, logger: this.logger });\n this.pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);\n this.concurrency = Math.min(MAX_CONCURRENCY, Math.max(1, options.concurrency ?? 1));\n this.agentName = options.agentName ?? defaultAgentName();\n this.taskTimeoutMs = options.taskTimeoutMs;\n this.logFlushIntervalMs = options.logFlushIntervalMs;\n this.logger.debug('agente configurado', { baseUrl: dsn.baseUrl, projectSlug: dsn.projectSlug });\n }\n\n get activeCount(): number {\n return this.activeRuns.size;\n }\n\n start(): void {\n if (this.state === 'started') return;\n if (this.state === 'stopping') {\n throw new Error('[hobots-manager] start() chamado durante stop() — aguarde o stop terminar');\n }\n if (this.registry.size === 0) {\n throw new Error('[hobots-manager] nenhuma task registrada — chame register() antes de start()');\n }\n this.state = 'started';\n this.logger.debug(`polling iniciado (tasks: ${[...this.registry.keys()].join(', ')})`);\n this.schedule(0);\n }\n\n /** Para o polling e aguarda as runs ativas (até timeoutMs). Retorna true se tudo terminou a tempo. */\n async stop(timeoutMs = 30_000): Promise<boolean> {\n if (this.state === 'idle') return true;\n this.state = 'stopping';\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n this.pollAbort?.abort();\n\n const all = Promise.allSettled([...this.activeRuns]).then(() => true);\n const finished = timeoutMs > 0 ? await Promise.race([all, sleep(timeoutMs).then(() => false)]) : false;\n if (!finished && this.activeRuns.size > 0) {\n this.logger.warn(`${this.activeRuns.size} run(s) ainda em execução após ${timeoutMs}ms — abandonando a espera`);\n for (const reporter of this.reporters) reporter.abort();\n }\n this.state = 'idle';\n return finished;\n }\n\n /** Timer intencionalmente NÃO unref'd: o agente é a razão do processo existir. */\n private schedule(delayMs: number): void {\n if (this.state !== 'started' || this.timer) return;\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.tick();\n }, delayMs);\n }\n\n private async tick(): Promise<void> {\n if (this.state !== 'started') return;\n if (this.activeRuns.size >= this.concurrency) {\n this.schedule(this.pollIntervalMs);\n return;\n }\n\n this.pollAbort = new AbortController();\n try {\n const response = await this.api.poll(\n {\n agent: {\n name: this.agentName,\n version: SDK_VERSION,\n host: defaultAgentName(),\n tasks: [...this.registry.keys()],\n },\n max: Math.min(MAX_CONCURRENCY, this.concurrency - this.activeRuns.size),\n tasks: [...this.registry.keys()],\n },\n this.pollAbort.signal\n );\n this.consecutiveFailures = 0;\n this.consecutiveAuthFailures = 0;\n\n for (const run of response.runs) {\n this.dispatch(run);\n }\n // com trabalho entregue, re-poll imediato (modo drain); sem trabalho, espera o intervalo\n this.schedule(response.runs.length > 0 ? 0 : this.pollIntervalMs);\n } catch (error) {\n if (this.state !== 'started') return; // stop() abortou o poll\n this.schedule(this.delayAfterPollError(error));\n } finally {\n this.pollAbort = undefined;\n }\n }\n\n private delayAfterPollError(error: unknown): number {\n if (error instanceof ManagerApiError && error.status === 401) {\n // chave desativada/rotacionada — continua tentando devagar (pode ser reativada)\n this.consecutiveAuthFailures++;\n if (this.consecutiveAuthFailures === 1 || this.consecutiveAuthFailures % 10 === 0) {\n this.logger.error('chave do agente rejeitada (401) — verifique a chave em /manager/agents');\n }\n return Math.max(this.pollIntervalMs, AUTH_FAILURE_INTERVAL_MS);\n }\n this.consecutiveFailures++;\n const ladder = POLL_RETRY_DELAYS_MS[Math.min(this.consecutiveFailures - 1, POLL_RETRY_DELAYS_MS.length - 1)]!;\n const delay = Math.min(Math.max(ladder, 0), Math.max(this.pollIntervalMs, 16_000));\n this.logger.warn(`poll falhou (${String(error)}), próxima tentativa em ${delay}ms`);\n return delay;\n }\n\n private dispatch(run: ManagerRunDescriptor): void {\n const promise = this.executeRun(run)\n .catch(error => this.logger.error(`erro inesperado na run ${run.id}:`, error))\n .finally(() => {\n this.activeRuns.delete(promise);\n if (this.state === 'started') this.schedule(0);\n });\n this.activeRuns.add(promise);\n }\n\n private async executeRun(run: ManagerRunDescriptor): Promise<void> {\n const reporter = new RunReporter(this.api, run.id, run.task, this.logger, this.logFlushIntervalMs);\n this.reporters.add(reporter);\n try {\n const handler = this.registry.get(run.task);\n if (!handler) {\n // claim veio para uma task sem handler (corrida rara) — falha direto, sem 'running'\n reporter.reportFailure({\n type: 'HandlerNotFound',\n message: `tarefa não registrada no agente: ${run.task}`,\n });\n await reporter.finalize();\n return;\n }\n\n const ok = await reporter.reportRunning();\n if (!ok) {\n this.logger.debug(`run ${run.id} cancelada antes de iniciar — handler não executado`);\n return;\n }\n\n this.logger.debug(`executando run ${run.id} (${run.task})`);\n try {\n const result = await this.invokeWithTimeout(handler, run, reporter);\n reporter.reportSuccess(result);\n this.logger.debug(`run ${run.id} concluída`);\n } catch (error) {\n reporter.reportFailure(toRunError(error));\n this.logger.debug(`run ${run.id} falhou: ${String(error)}`);\n }\n await reporter.finalize();\n } finally {\n this.reporters.delete(reporter);\n }\n }\n\n private async invokeWithTimeout(\n handler: ManagerTaskHandler,\n run: ManagerRunDescriptor,\n reporter: RunReporter\n ): Promise<unknown> {\n const invocation = Promise.resolve().then(() => handler(run.params, reporter.context));\n if (!this.taskTimeoutMs || this.taskTimeoutMs <= 0) return invocation;\n\n const timeoutMs = this.taskTimeoutMs;\n const timeout = sleep(timeoutMs).then(() => TIMEOUT_SENTINEL);\n const outcome = await Promise.race([invocation, timeout]);\n if (outcome !== TIMEOUT_SENTINEL) return outcome;\n\n // não dá para matar o código do usuário — reporta, aborta o signal e libera o slot\n reporter.abort();\n invocation\n .then(() => this.logger.warn(`run ${run.id} terminou depois do timeout de ${timeoutMs}ms (resultado descartado)`))\n .catch(() => this.logger.warn(`run ${run.id} falhou depois do timeout de ${timeoutMs}ms (erro descartado)`));\n throw Object.assign(new Error(`timeout após ${timeoutMs}ms (o handler pode continuar em background)`), {\n name: 'TaskTimeout',\n });\n }\n}\n\nconst TIMEOUT_SENTINEL = Symbol('task-timeout');\n\nconst MAX_ERROR_MESSAGE_CHARS = 10_000;\nconst MAX_ERROR_STACK_CHARS = 20_000;\n\nfunction truncate(value: string, max: number): string {\n return value.length > max ? `${value.slice(0, max)}… [truncado]` : value;\n}\n\nfunction toRunError(error: unknown): ManagerRunError {\n if (error instanceof Error) {\n return {\n type: error.name,\n message: truncate(error.message, MAX_ERROR_MESSAGE_CHARS),\n stack: error.stack ? truncate(error.stack, MAX_ERROR_STACK_CHARS) : undefined,\n };\n }\n return { message: truncate(String(error), MAX_ERROR_MESSAGE_CHARS) };\n}\n","import type { ManagerInitOptions, ManagerTaskHandler } from './types';\nimport { ManagerAgent } from './agent';\n\nlet agent: ManagerAgent | undefined;\nconst registry = new Map<string, ManagerTaskHandler>();\n\n/** Inicializa o agente Manager. Chame uma vez; depois register() e start(). */\nexport function init(options: ManagerInitOptions): void {\n if (agent) {\n agent.logger.warn('init() chamado mais de uma vez — substituindo o agente anterior');\n void agent.stop(0);\n }\n agent = new ManagerAgent(options, registry);\n}\n\n/** Registra o handler de uma task (o task_slug configurado no formulário do app). */\nexport function register<P = Record<string, unknown>, R = unknown>(task: string, handler: ManagerTaskHandler<P, R>): void {\n if (registry.has(task)) {\n agent?.logger.warn(`register('${task}') sobrescreveu um handler existente`);\n }\n registry.set(task, handler as ManagerTaskHandler);\n}\n\n/**\n * Inicia o polling. O timer NÃO é unref'd: o processo fica vivo aguardando\n * trabalho até stop() — o inverso deliberado do Monitor.\n */\nexport function start(): void {\n if (!agent) {\n throw new Error('[hobots-manager] init() precisa ser chamado antes de start()');\n }\n agent.start();\n}\n\n/** Para o polling e aguarda a run atual terminar (até timeoutMs). */\nexport function stop(timeoutMs?: number): Promise<boolean> {\n return agent ? agent.stop(timeoutMs) : Promise.resolve(true);\n}\n"],"mappings":"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","names":["registry"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/protocol.ts
|
|
17
|
+
var protocol_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(protocol_exports);
|
|
19
|
+
//# sourceMappingURL=protocol.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Tipos do protocolo de ingestão do Hobots Monitor (SDK → servidor).\n *\n * Este arquivo é a fonte de verdade do wire format e não pode importar nada:\n * o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`\n * contra estes tipos (importados de `hobots/protocol`).\n */\n\nexport type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug';\n\n/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */\nexport interface StackFrame {\n function?: string;\n filename?: string;\n lineno?: number;\n colno?: number;\n in_app: boolean;\n}\n\nexport interface Breadcrumb {\n /** ISO 8601 */\n timestamp: string;\n type?: string;\n category?: string;\n message?: string;\n level?: Severity;\n data?: Record<string, unknown>;\n}\n\nexport interface ExceptionData {\n type: string;\n value: string;\n stacktrace?: { frames: StackFrame[] };\n}\n\nexport interface EventUser {\n id?: string;\n username?: string;\n email?: string;\n}\n\nexport interface EventPayload {\n /** uuid v4 sem hífens (32 hex), gerado pelo SDK */\n event_id: string;\n /** ISO 8601 */\n timestamp: string;\n level: Severity;\n message?: string;\n exception?: ExceptionData;\n /** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */\n fingerprint?: string[];\n breadcrumbs?: Breadcrumb[];\n tags?: Record<string, string>;\n user?: EventUser;\n contexts?: Record<string, Record<string, unknown>>;\n extra?: Record<string, unknown>;\n release?: string;\n environment?: string;\n server_name?: string;\n}\n\nexport interface SpanData {\n /** 16 hex */\n span_id: string;\n parent_span_id?: string;\n op?: string;\n description?: string;\n status?: string;\n /** ISO 8601 */\n start_timestamp: string;\n /** ISO 8601 (fim do span) */\n timestamp: string;\n data?: Record<string, unknown>;\n}\n\nexport interface TransactionPayload {\n event_id: string;\n name: string;\n op?: string;\n status: string;\n start_timestamp: string;\n timestamp: string;\n spans: SpanData[];\n tags?: Record<string, string>;\n release?: string;\n environment?: string;\n server_name?: string;\n}\n\nexport type CheckInStatus = 'in_progress' | 'ok' | 'error';\n\nexport interface CheckInPayload {\n /** Mesmo id no par in_progress → ok/error para casar início e fim */\n check_in_id: string;\n monitor_slug: string;\n status: CheckInStatus;\n duration_ms?: number;\n /** Expressão cron — permite o servidor criar o monitor automaticamente (upsert) */\n schedule?: string;\n timezone?: string;\n environment?: string;\n}\n\nexport interface HeartbeatPayload {\n instance_id: string;\n name?: string;\n interval_ms: number;\n /** ISO 8601 */\n timestamp: string;\n metadata?: Record<string, unknown>;\n}\n\nexport type EnvelopeItem =\n | { type: 'event'; payload: EventPayload }\n | { type: 'transaction'; payload: TransactionPayload }\n | { type: 'check_in'; payload: CheckInPayload }\n | { type: 'heartbeat'; payload: HeartbeatPayload };\n\nexport interface Envelope {\n sdk: { name: string; version: string };\n /** ISO 8601 */\n sent_at: string;\n items: EnvelopeItem[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos do protocolo de ingestão do Hobots Monitor (SDK → servidor).
|
|
3
|
+
*
|
|
4
|
+
* Este arquivo é a fonte de verdade do wire format e não pode importar nada:
|
|
5
|
+
* o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`
|
|
6
|
+
* contra estes tipos (importados de `hobots/protocol`).
|
|
7
|
+
*/
|
|
8
|
+
type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug';
|
|
9
|
+
/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */
|
|
10
|
+
interface StackFrame {
|
|
11
|
+
function?: string;
|
|
12
|
+
filename?: string;
|
|
13
|
+
lineno?: number;
|
|
14
|
+
colno?: number;
|
|
15
|
+
in_app: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface Breadcrumb {
|
|
18
|
+
/** ISO 8601 */
|
|
19
|
+
timestamp: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
category?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
level?: Severity;
|
|
24
|
+
data?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
interface ExceptionData {
|
|
27
|
+
type: string;
|
|
28
|
+
value: string;
|
|
29
|
+
stacktrace?: {
|
|
30
|
+
frames: StackFrame[];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
interface EventUser {
|
|
34
|
+
id?: string;
|
|
35
|
+
username?: string;
|
|
36
|
+
email?: string;
|
|
37
|
+
}
|
|
38
|
+
interface EventPayload {
|
|
39
|
+
/** uuid v4 sem hífens (32 hex), gerado pelo SDK */
|
|
40
|
+
event_id: string;
|
|
41
|
+
/** ISO 8601 */
|
|
42
|
+
timestamp: string;
|
|
43
|
+
level: Severity;
|
|
44
|
+
message?: string;
|
|
45
|
+
exception?: ExceptionData;
|
|
46
|
+
/** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */
|
|
47
|
+
fingerprint?: string[];
|
|
48
|
+
breadcrumbs?: Breadcrumb[];
|
|
49
|
+
tags?: Record<string, string>;
|
|
50
|
+
user?: EventUser;
|
|
51
|
+
contexts?: Record<string, Record<string, unknown>>;
|
|
52
|
+
extra?: Record<string, unknown>;
|
|
53
|
+
release?: string;
|
|
54
|
+
environment?: string;
|
|
55
|
+
server_name?: string;
|
|
56
|
+
}
|
|
57
|
+
interface SpanData {
|
|
58
|
+
/** 16 hex */
|
|
59
|
+
span_id: string;
|
|
60
|
+
parent_span_id?: string;
|
|
61
|
+
op?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
status?: string;
|
|
64
|
+
/** ISO 8601 */
|
|
65
|
+
start_timestamp: string;
|
|
66
|
+
/** ISO 8601 (fim do span) */
|
|
67
|
+
timestamp: string;
|
|
68
|
+
data?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
interface TransactionPayload {
|
|
71
|
+
event_id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
op?: string;
|
|
74
|
+
status: string;
|
|
75
|
+
start_timestamp: string;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
spans: SpanData[];
|
|
78
|
+
tags?: Record<string, string>;
|
|
79
|
+
release?: string;
|
|
80
|
+
environment?: string;
|
|
81
|
+
server_name?: string;
|
|
82
|
+
}
|
|
83
|
+
type CheckInStatus = 'in_progress' | 'ok' | 'error';
|
|
84
|
+
interface CheckInPayload {
|
|
85
|
+
/** Mesmo id no par in_progress → ok/error para casar início e fim */
|
|
86
|
+
check_in_id: string;
|
|
87
|
+
monitor_slug: string;
|
|
88
|
+
status: CheckInStatus;
|
|
89
|
+
duration_ms?: number;
|
|
90
|
+
/** Expressão cron — permite o servidor criar o monitor automaticamente (upsert) */
|
|
91
|
+
schedule?: string;
|
|
92
|
+
timezone?: string;
|
|
93
|
+
environment?: string;
|
|
94
|
+
}
|
|
95
|
+
interface HeartbeatPayload {
|
|
96
|
+
instance_id: string;
|
|
97
|
+
name?: string;
|
|
98
|
+
interval_ms: number;
|
|
99
|
+
/** ISO 8601 */
|
|
100
|
+
timestamp: string;
|
|
101
|
+
metadata?: Record<string, unknown>;
|
|
102
|
+
}
|
|
103
|
+
type EnvelopeItem = {
|
|
104
|
+
type: 'event';
|
|
105
|
+
payload: EventPayload;
|
|
106
|
+
} | {
|
|
107
|
+
type: 'transaction';
|
|
108
|
+
payload: TransactionPayload;
|
|
109
|
+
} | {
|
|
110
|
+
type: 'check_in';
|
|
111
|
+
payload: CheckInPayload;
|
|
112
|
+
} | {
|
|
113
|
+
type: 'heartbeat';
|
|
114
|
+
payload: HeartbeatPayload;
|
|
115
|
+
};
|
|
116
|
+
interface Envelope {
|
|
117
|
+
sdk: {
|
|
118
|
+
name: string;
|
|
119
|
+
version: string;
|
|
120
|
+
};
|
|
121
|
+
/** ISO 8601 */
|
|
122
|
+
sent_at: string;
|
|
123
|
+
items: EnvelopeItem[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type { Breadcrumb, CheckInPayload, CheckInStatus, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, Severity, SpanData, StackFrame, TransactionPayload };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tipos do protocolo de ingestão do Hobots Monitor (SDK → servidor).
|
|
3
|
+
*
|
|
4
|
+
* Este arquivo é a fonte de verdade do wire format e não pode importar nada:
|
|
5
|
+
* o app valida os payloads com zod e trava o drift via `satisfies z.ZodType<...>`
|
|
6
|
+
* contra estes tipos (importados de `hobots/protocol`).
|
|
7
|
+
*/
|
|
8
|
+
type Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug';
|
|
9
|
+
/** Frames em ordem innermost-first (mesma ordem impressa pelo V8). */
|
|
10
|
+
interface StackFrame {
|
|
11
|
+
function?: string;
|
|
12
|
+
filename?: string;
|
|
13
|
+
lineno?: number;
|
|
14
|
+
colno?: number;
|
|
15
|
+
in_app: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface Breadcrumb {
|
|
18
|
+
/** ISO 8601 */
|
|
19
|
+
timestamp: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
category?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
level?: Severity;
|
|
24
|
+
data?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
interface ExceptionData {
|
|
27
|
+
type: string;
|
|
28
|
+
value: string;
|
|
29
|
+
stacktrace?: {
|
|
30
|
+
frames: StackFrame[];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
interface EventUser {
|
|
34
|
+
id?: string;
|
|
35
|
+
username?: string;
|
|
36
|
+
email?: string;
|
|
37
|
+
}
|
|
38
|
+
interface EventPayload {
|
|
39
|
+
/** uuid v4 sem hífens (32 hex), gerado pelo SDK */
|
|
40
|
+
event_id: string;
|
|
41
|
+
/** ISO 8601 */
|
|
42
|
+
timestamp: string;
|
|
43
|
+
level: Severity;
|
|
44
|
+
message?: string;
|
|
45
|
+
exception?: ExceptionData;
|
|
46
|
+
/** Override de agrupamento; quando ausente o servidor calcula pelo stack trace */
|
|
47
|
+
fingerprint?: string[];
|
|
48
|
+
breadcrumbs?: Breadcrumb[];
|
|
49
|
+
tags?: Record<string, string>;
|
|
50
|
+
user?: EventUser;
|
|
51
|
+
contexts?: Record<string, Record<string, unknown>>;
|
|
52
|
+
extra?: Record<string, unknown>;
|
|
53
|
+
release?: string;
|
|
54
|
+
environment?: string;
|
|
55
|
+
server_name?: string;
|
|
56
|
+
}
|
|
57
|
+
interface SpanData {
|
|
58
|
+
/** 16 hex */
|
|
59
|
+
span_id: string;
|
|
60
|
+
parent_span_id?: string;
|
|
61
|
+
op?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
status?: string;
|
|
64
|
+
/** ISO 8601 */
|
|
65
|
+
start_timestamp: string;
|
|
66
|
+
/** ISO 8601 (fim do span) */
|
|
67
|
+
timestamp: string;
|
|
68
|
+
data?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
interface TransactionPayload {
|
|
71
|
+
event_id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
op?: string;
|
|
74
|
+
status: string;
|
|
75
|
+
start_timestamp: string;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
spans: SpanData[];
|
|
78
|
+
tags?: Record<string, string>;
|
|
79
|
+
release?: string;
|
|
80
|
+
environment?: string;
|
|
81
|
+
server_name?: string;
|
|
82
|
+
}
|
|
83
|
+
type CheckInStatus = 'in_progress' | 'ok' | 'error';
|
|
84
|
+
interface CheckInPayload {
|
|
85
|
+
/** Mesmo id no par in_progress → ok/error para casar início e fim */
|
|
86
|
+
check_in_id: string;
|
|
87
|
+
monitor_slug: string;
|
|
88
|
+
status: CheckInStatus;
|
|
89
|
+
duration_ms?: number;
|
|
90
|
+
/** Expressão cron — permite o servidor criar o monitor automaticamente (upsert) */
|
|
91
|
+
schedule?: string;
|
|
92
|
+
timezone?: string;
|
|
93
|
+
environment?: string;
|
|
94
|
+
}
|
|
95
|
+
interface HeartbeatPayload {
|
|
96
|
+
instance_id: string;
|
|
97
|
+
name?: string;
|
|
98
|
+
interval_ms: number;
|
|
99
|
+
/** ISO 8601 */
|
|
100
|
+
timestamp: string;
|
|
101
|
+
metadata?: Record<string, unknown>;
|
|
102
|
+
}
|
|
103
|
+
type EnvelopeItem = {
|
|
104
|
+
type: 'event';
|
|
105
|
+
payload: EventPayload;
|
|
106
|
+
} | {
|
|
107
|
+
type: 'transaction';
|
|
108
|
+
payload: TransactionPayload;
|
|
109
|
+
} | {
|
|
110
|
+
type: 'check_in';
|
|
111
|
+
payload: CheckInPayload;
|
|
112
|
+
} | {
|
|
113
|
+
type: 'heartbeat';
|
|
114
|
+
payload: HeartbeatPayload;
|
|
115
|
+
};
|
|
116
|
+
interface Envelope {
|
|
117
|
+
sdk: {
|
|
118
|
+
name: string;
|
|
119
|
+
version: string;
|
|
120
|
+
};
|
|
121
|
+
/** ISO 8601 */
|
|
122
|
+
sent_at: string;
|
|
123
|
+
items: EnvelopeItem[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type { Breadcrumb, CheckInPayload, CheckInStatus, Envelope, EnvelopeItem, EventPayload, EventUser, ExceptionData, HeartbeatPayload, Severity, SpanData, StackFrame, TransactionPayload };
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Documentação — hobots
|
|
2
|
+
|
|
3
|
+
SDK JavaScript da **Hobots** para instrumentar funcionários digitais (RPAs) e integrações. Cliente HTTP puro, sem dependências de runtime, para Node 18+ (o Monitor também roda no browser para captura de erros).
|
|
4
|
+
|
|
5
|
+
O SDK expõe **duas capabilities independentes** — credenciais, entry points e ciclos de vida separados — que podem conviver no mesmo processo:
|
|
6
|
+
|
|
7
|
+
| Capability | Import | Para quê |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| **Monitor** | `hobots` | Telemetria que o programa **emite** sozinho: erros com stack trace, performance (transactions/spans), check-ins de cron e heartbeats de uptime. Instala handlers globais de erro; os timers são `unref`'d, então nunca seguram o processo vivo. |
|
|
10
|
+
| **Manager** | `hobots/manager` | Execução **sob demanda**: o agente faz polling no app, executa handlers registrados e reporta status/logs/resultado. `start()` **mantém o processo vivo** até `stop()`. |
|
|
11
|
+
|
|
12
|
+
## Páginas
|
|
13
|
+
|
|
14
|
+
- [Getting started](./getting-started.md) — instalação, DSN, primeiro `init`, Monitor vs Manager.
|
|
15
|
+
- [Monitor](./monitor.md) — erros, breadcrumbs, escopo, transactions/spans, crons e heartbeat.
|
|
16
|
+
- [Manager](./manager.md) — registro de handlers, `RunContext`, cancelamento, concorrência, idempotência.
|
|
17
|
+
- [Referência de API](./api-reference.md) — todas as funções e tabelas de opções com defaults.
|
|
18
|
+
- [Protocolo & transporte](./protocol.md) — DSNs, endpoints, envelope, retries e onde vive o claim atômico.
|
|
19
|
+
|
|
20
|
+
## Exemplo mínimo
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import * as Monitor from 'hobots';
|
|
24
|
+
import * as Manager from 'hobots/manager';
|
|
25
|
+
|
|
26
|
+
// Telemetria (opcional, mas recomendado)
|
|
27
|
+
Monitor.init({ dsn: 'http://<publicKey>@localhost:3002/<projectId>' });
|
|
28
|
+
|
|
29
|
+
// Execução remota
|
|
30
|
+
Manager.init({ dsn: 'http://<managerKey>@localhost:3002/<projectSlug>' });
|
|
31
|
+
Manager.register('processar-nfe', async (params, ctx) => {
|
|
32
|
+
ctx.log('info', `processando lote ${params.lote}…`);
|
|
33
|
+
return { processadas: 12 };
|
|
34
|
+
});
|
|
35
|
+
Manager.start();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> Um exemplo completo das duas capabilities juntas está em
|
|
39
|
+
> [`apps/webapp/scripts/demo.ts`](../../../apps/webapp/scripts/demo.ts) (`pnpm --filter @hobots/webapp demo`).
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Referência de API
|
|
2
|
+
|
|
3
|
+
## Entry points
|
|
4
|
+
|
|
5
|
+
| Import | Conteúdo |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `hobots` | Monitor: funções, `Client`, `Scope`, `Transaction`, `Span`, `parseDsn`, tipos. |
|
|
8
|
+
| `hobots/protocol` | Tipos do wire format do Monitor. |
|
|
9
|
+
| `hobots/manager` | Manager: `init`/`register`/`start`/`stop`, `ManagerAgent`, `ManagerApi`, tipos. |
|
|
10
|
+
| `hobots/manager/protocol` | Tipos do wire format do Manager. |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Monitor — funções
|
|
15
|
+
|
|
16
|
+
| Função | Assinatura | Retorno |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `init` | `init(options: InitOptions): void` | — |
|
|
19
|
+
| `getClient` | `getClient(): Client \| undefined` | cliente atual |
|
|
20
|
+
| `captureException` | `captureException(error: unknown, hint?: CaptureHint)` | `string \| undefined` (event_id) |
|
|
21
|
+
| `captureMessage` | `captureMessage(message: string, level?: Severity, hint?: CaptureHint)` | `string \| undefined` |
|
|
22
|
+
| `addBreadcrumb` | `addBreadcrumb(crumb: BreadcrumbInput): void` | — |
|
|
23
|
+
| `setTag` / `setTags` | `setTag(key, value)` · `setTags(tags)` | — |
|
|
24
|
+
| `setUser` | `setUser(user: EventUser \| null): void` | — |
|
|
25
|
+
| `setContext` | `setContext(name: string, ctx: Record<string,unknown> \| null): void` | — |
|
|
26
|
+
| `setExtra` | `setExtra(key: string, value: unknown): void` | — |
|
|
27
|
+
| `withScope` | `withScope<T>(cb: (scope: Scope) => T): T` | retorno do callback |
|
|
28
|
+
| `startTransaction` | `startTransaction(name: string, op?: string): Transaction` | transaction |
|
|
29
|
+
| `checkIn` | `checkIn(options: CheckInOptions): string \| undefined` | check_in_id |
|
|
30
|
+
| `withMonitor` | `withMonitor<T>(slug: string, fn: () => T \| Promise<T>, options?: WithMonitorOptions): Promise<T>` | retorno de `fn` |
|
|
31
|
+
| `flush` | `flush(timeoutMs?: number): Promise<boolean>` | enviou tudo? |
|
|
32
|
+
| `close` | `close(timeoutMs?: number): Promise<boolean>` | flush + para heartbeat/transport |
|
|
33
|
+
|
|
34
|
+
Classes exportadas: `Client`, `Scope`, `Transaction`, `Span`. Função `parseDsn(dsn): ParsedDsn`.
|
|
35
|
+
|
|
36
|
+
### Métodos de `Transaction` / `Span`
|
|
37
|
+
|
|
38
|
+
- `Transaction`: `startChild(op?, description?) → Span`, `setTag(k, v)`, `setStatus(s)`, `setName(n)`, `finish()`.
|
|
39
|
+
- `Span`: `startChild(op?, description?) → Span`, `setStatus(s)`, `setData(k, v)`, `finish()`, `toJSON()`.
|
|
40
|
+
- `Scope`: `setTag/setTags/setUser/setContext/setExtra/setExtras/setLevel/setFingerprint/addBreadcrumb/clearBreadcrumbs/clone` (todos encadeáveis).
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Manager — funções
|
|
45
|
+
|
|
46
|
+
| Função | Assinatura |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `init` | `init(options: ManagerInitOptions): void` |
|
|
49
|
+
| `register` | `register<P, R>(task: string, handler: ManagerTaskHandler<P, R>): void` |
|
|
50
|
+
| `start` | `start(): void` |
|
|
51
|
+
| `stop` | `stop(timeoutMs?: number): Promise<boolean>` |
|
|
52
|
+
|
|
53
|
+
Classes/erros exportados: `ManagerAgent`, `ManagerApi`, `ManagerApiError`. Função `parseManagerDsn(dsn): ParsedManagerDsn`.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Tabelas de opções
|
|
58
|
+
|
|
59
|
+
### InitOptions
|
|
60
|
+
|
|
61
|
+
| Opção | Tipo | Default | Descrição |
|
|
62
|
+
| --- | --- | --- | --- |
|
|
63
|
+
| `dsn` | `string` | — | **obrigatório**; `<origin>/<publicKey>/<projectId>` |
|
|
64
|
+
| `environment` | `string` | `'production'` | ambiente lógico |
|
|
65
|
+
| `release` | `string` | — | versão do programa, ex. `robo-nfe@1.4.2` |
|
|
66
|
+
| `serverName` | `string` | hostname | identifica a máquina; também vira `instanceId` do heartbeat |
|
|
67
|
+
| `tags` | `Record<string,string>` | — | tags aplicadas a todos os eventos |
|
|
68
|
+
| `sampleRate` | `number` | `1` | amostragem de erros (0..1) |
|
|
69
|
+
| `tracesSampleRate` | `number` | `1` | amostragem de transactions (0..1) |
|
|
70
|
+
| `maxBreadcrumbs` | `number` | `100` | tamanho do buffer de breadcrumbs |
|
|
71
|
+
| `captureUnhandled` | `boolean` | `true` | handlers globais de erro |
|
|
72
|
+
| `heartbeat` | `boolean \| HeartbeatOptions` | `false` | heartbeat periódico |
|
|
73
|
+
| `beforeSend` | `(event) => event \| null` | — | edita/descarta evento (retorne `null` para descartar) |
|
|
74
|
+
| `debug` | `boolean` | `false` | loga atividade do SDK |
|
|
75
|
+
| `flushIntervalMs` | `number` | `2000` | intervalo do flush automático do transport |
|
|
76
|
+
| `maxBatchSize` | `number` | `10` | itens por envelope antes do flush imediato |
|
|
77
|
+
|
|
78
|
+
### HeartbeatOptions
|
|
79
|
+
|
|
80
|
+
| Opção | Tipo | Default | Descrição |
|
|
81
|
+
| --- | --- | --- | --- |
|
|
82
|
+
| `intervalMs` | `number` | `30000` | intervalo entre batimentos |
|
|
83
|
+
| `instanceId` | `string` | `serverName`/`HOSTNAME`/id aleatório | identificador estável da instância |
|
|
84
|
+
| `name` | `string` | — | nome amigável no dashboard |
|
|
85
|
+
| `metadata` | `Record<string,unknown>` | — | metadados anexados ao batimento |
|
|
86
|
+
|
|
87
|
+
### CaptureHint
|
|
88
|
+
|
|
89
|
+
| Campo | Tipo | Descrição |
|
|
90
|
+
| --- | --- | --- |
|
|
91
|
+
| `level` | `Severity` | nível do evento |
|
|
92
|
+
| `tags` | `Record<string,string>` | tags só deste evento |
|
|
93
|
+
| `extra` | `Record<string,unknown>` | dados extras |
|
|
94
|
+
| `fingerprint` | `string[]` | agrupamento manual |
|
|
95
|
+
|
|
96
|
+
`Severity = 'fatal' | 'error' | 'warning' | 'info' | 'debug'`.
|
|
97
|
+
|
|
98
|
+
### CheckInOptions
|
|
99
|
+
|
|
100
|
+
| Campo | Tipo | Descrição |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `monitorSlug` | `string` | **obrigatório**; identifica o monitor |
|
|
103
|
+
| `status` | `'in_progress' \| 'ok' \| 'error'` | **obrigatório** |
|
|
104
|
+
| `checkInId` | `string` | reutilize o id do `in_progress` para fechar a execução |
|
|
105
|
+
| `durationMs` | `number` | duração medida |
|
|
106
|
+
| `schedule` | `string` | expressão cron — cria/atualiza o monitor no servidor (upsert) |
|
|
107
|
+
| `timezone` | `string` | fuso do `schedule` |
|
|
108
|
+
|
|
109
|
+
### WithMonitorOptions
|
|
110
|
+
|
|
111
|
+
| Campo | Tipo | Descrição |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `schedule` | `string` | expressão cron (upsert do monitor) |
|
|
114
|
+
| `timezone` | `string` | fuso do `schedule` |
|
|
115
|
+
|
|
116
|
+
### ManagerInitOptions
|
|
117
|
+
|
|
118
|
+
| Opção | Tipo | Default | Descrição |
|
|
119
|
+
| --- | --- | --- | --- |
|
|
120
|
+
| `dsn` | `string` | — | **obrigatório**, com a **manager key** (não a chave de ingestão) |
|
|
121
|
+
| `pollIntervalMs` | `number` | `5000` | intervalo entre polls sem trabalho (mínimo `1000`) |
|
|
122
|
+
| `concurrency` | `number` | `1` | runs simultâneas (clamp 1..5) |
|
|
123
|
+
| `agentName` | `string` | hostname | nome exibido no dashboard |
|
|
124
|
+
| `taskTimeoutMs` | `number` | — | timeout client-side por run (não mata o handler; servidor é o backstop) |
|
|
125
|
+
| `logFlushIntervalMs` | `number` | `2000` | intervalo do envio incremental de logs |
|
|
126
|
+
| `debug` | `boolean` | `false` | loga atividade do agente |
|
|
127
|
+
|
|
128
|
+
### ManagerTaskHandler / RunContext
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
type ManagerTaskHandler<P = Record<string, unknown>, R = unknown> =
|
|
132
|
+
(params: P, ctx: RunContext) => R | Promise<R>;
|
|
133
|
+
|
|
134
|
+
interface RunContext {
|
|
135
|
+
runId: string;
|
|
136
|
+
task: string;
|
|
137
|
+
log(level: 'debug' | 'info' | 'warning' | 'error', message: string): void;
|
|
138
|
+
signal: AbortSignal;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
## Instalação
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add hobots
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Sem dependências de runtime. Requer **Node 18+** (usa o `fetch` global). O Monitor também funciona no browser para captura de erros.
|
|
10
|
+
|
|
11
|
+
## O DSN
|
|
12
|
+
|
|
13
|
+
Toda credencial do SDK é um **DSN** no formato `<protocolo>://<key>@<host>[:porta]/<projeto>`. A `key` vai embutida na URL e o `host` aponta para o app da Hobots.
|
|
14
|
+
|
|
15
|
+
- **Monitor** usa a **public key** (chave de ingestão) e o `projectId`. Você a copia da página do projeto em `/monitor` — o snippet de integração já vem pronto.
|
|
16
|
+
- **Manager** usa a **manager key** (chave secreta do agente, criada em `/manager/agents`) e o `projectSlug`. **Não** é a chave de ingestão.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Monitor: http://<publicKey>@localhost:3002/<projectId>
|
|
20
|
+
Manager: http://<managerKey>@localhost:3002/<projectSlug>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Veja [Protocolo & transporte](./protocol.md) para os endpoints que cada DSN deriva.
|
|
24
|
+
|
|
25
|
+
## Monitor vs Manager — qual usar
|
|
26
|
+
|
|
27
|
+
As duas capabilities são independentes e você usa **uma, outra ou as duas**:
|
|
28
|
+
|
|
29
|
+
- Quer só saber se o robô quebrou, quanto demora e se está vivo? → **Monitor**.
|
|
30
|
+
- Quer disparar o robô sob demanda a partir de um formulário no app? → **Manager**.
|
|
31
|
+
- Ambos? Inicialize os dois no mesmo processo (credenciais separadas). É o cenário do
|
|
32
|
+
[`demo.ts`](../../../apps/webapp/scripts/demo.ts).
|
|
33
|
+
|
|
34
|
+
## Primeiro `init` (Monitor)
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
import * as Monitor from 'hobots';
|
|
38
|
+
|
|
39
|
+
Monitor.init({
|
|
40
|
+
dsn: 'http://<publicKey>@localhost:3002/<projectId>',
|
|
41
|
+
environment: 'production',
|
|
42
|
+
release: 'robo-nfe@1.4.2',
|
|
43
|
+
serverName: 'vm-cliente-01',
|
|
44
|
+
heartbeat: true, // "estou vivo" a cada 30s
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Erros não tratados são capturados automaticamente.
|
|
48
|
+
// Captura manual:
|
|
49
|
+
try {
|
|
50
|
+
await processarNota(nota);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
Monitor.captureException(error, { tags: { cliente: 'acme' } });
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Chame `Monitor.init` **uma vez**, o mais cedo possível no processo. Detalhes em [Monitor](./monitor.md).
|
|
58
|
+
|
|
59
|
+
## Primeiro agente (Manager)
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import * as Manager from 'hobots/manager';
|
|
63
|
+
|
|
64
|
+
Manager.init({ dsn: 'http://<managerKey>@localhost:3002/<projectSlug>', agentName: 'VM Cliente 01' });
|
|
65
|
+
Manager.register('processar-nfe', async (params, ctx) => {
|
|
66
|
+
ctx.log('info', `processando lote ${params.lote}…`);
|
|
67
|
+
return { processadas: 12 };
|
|
68
|
+
});
|
|
69
|
+
Manager.start(); // mantém o processo vivo até stop()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Detalhes (cancelamento, concorrência, idempotência) em [Manager](./manager.md).
|
|
73
|
+
|
|
74
|
+
## Encerramento
|
|
75
|
+
|
|
76
|
+
O **Monitor** não segura o processo (timers `unref`'d), mas faça o flush final para não perder eventos em buffer:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
await Monitor.close(2000);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
O **Manager** segura o processo por design. Encerre de forma graciosa aguardando a run atual:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
process.on('SIGINT', () => {
|
|
86
|
+
void Manager.stop(15_000).then(() => process.exit(0));
|
|
87
|
+
});
|
|
88
|
+
```
|