hobots 1.1.0 → 1.1.1

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 CHANGED
@@ -26,7 +26,7 @@ Também funciona em CommonJS: `const Hobots = require('hobots')`.
26
26
 
27
27
  ## Um SDK, duas capabilities, um `init()`
28
28
 
29
- O SDK expõe **duas capabilities** autenticadas pela **mesma DSN única do projeto**, configuradas em um único `init()`:
29
+ O SDK expõe **duas capabilities** autenticadas pelo **mesmo Client Secret do projeto**, configuradas em um único `init()`:
30
30
 
31
31
  | Capability | Como habilita | Para quê | Ciclo de vida |
32
32
  | --- | --- | --- | --- |
@@ -35,13 +35,13 @@ O SDK expõe **duas capabilities** autenticadas pela **mesma DSN única do proje
35
35
 
36
36
  Você usa **uma, outra ou as duas**:
37
37
 
38
- - Só saber se o robô quebrou, quanto demora e se está vivo? → `init({ dsn, heartbeat: true })`.
39
- - Disparar o robô sob demanda a partir de um formulário no app? → `init({ dsn, manager: true })` + `register()` + `start()`.
38
+ - Só saber se o robô quebrou, quanto demora e se está vivo? → `init({ clientSecret, heartbeat: true })`.
39
+ - Disparar o robô sob demanda a partir de um formulário no app? → `init({ clientSecret, manager: true })` + `register()` + `start()`.
40
40
  - Ambos? Passe `heartbeat` e `manager` no mesmo `init()` — o `instanceId` é a identidade única, e os dois canais convergem em **um agente** no painel.
41
41
 
42
42
  ```js
43
43
  Hobots.init({
44
- dsn: 'https://<secretKey>@<host>/<projectSlug>',
44
+ clientSecret: 'hb_<64 hex>',
45
45
  instanceId: 'vm-cliente-01',
46
46
  heartbeat: true, // Monitor: "estou vivo" a cada 30s
47
47
  manager: { pollIntervalMs: 5_000 }, // Manager: execução sob demanda
@@ -57,15 +57,15 @@ Hobots.start(); // inicia o polling do Manager (segura o processo vivo)
57
57
 
58
58
  ---
59
59
 
60
- ## O DSN
60
+ ## O Client Secret
61
61
 
62
- Cada projeto tem **uma única DSN** — a mesma credencial autentica o Monitor **e** o Manager:
62
+ Cada projeto tem **um único Client Secret** — a mesma credencial autentica o Monitor **e** o Manager:
63
63
 
64
64
  ```
65
- https://<secretKey>@<host>/<projectSlug>
65
+ hb_<64 hex>
66
66
  ```
67
67
 
68
- A `secretKey` (64 hex) é **secreta**: vai embutida na DSN só por conveniência — nas requisições viaja sempre no header `Authorization: Bearer`, nunca na URL. O `projectSlug` do path é informativo; a identidade do projeto vem da chave. Copie a DSN pronta no app, em **Configurações → Agentes** — os snippets de integração já vêm prontos.
68
+ (Chaves antigas, 64 hex puros, continuam válidas no servidor.) A credencial é **secreta** e viaja sempre no header `Authorization: Bearer` — nunca na URL. Copie o Client Secret no app, em **Configurações → Projetos** — os snippets de integração já vêm prontos. O SDK fala com a API oficial `https://api.hobots.app` automaticamente; em desenvolvimento, aponte para outro host com a env `HOBOTS_API_URL` (ex.: `HOBOTS_API_URL=http://localhost:3001`).
69
69
 
70
70
  ---
71
71
 
@@ -79,7 +79,7 @@ Inicialize **uma vez**, o mais cedo possível no processo. Cria o cliente, insta
79
79
 
80
80
  ```js
81
81
  Hobots.init({
82
- dsn: 'https://<secretKey>@<host>/<projectSlug>',
82
+ clientSecret: 'hb_<64 hex>',
83
83
  environment: 'production',
84
84
  release: 'robo-nfe@1.4.2',
85
85
  instanceId: 'vm-cliente-01',
@@ -90,7 +90,7 @@ Hobots.init({
90
90
 
91
91
  | Opção | Tipo | Default | Descrição |
92
92
  | --- | --- | --- | --- |
93
- | `dsn` | `string` | **obrigatório** | a DSN única do projeto: `https://<secretKey>@host/<projectSlug>` |
93
+ | `clientSecret` | `string` | **obrigatório** | o Client Secret do projeto (`hb_<64 hex>`), copiado em **Configurações → Projetos** |
94
94
  | `environment` | `string` | `'production'` | ambiente lógico |
95
95
  | `release` | `string` | — | versão do programa, ex. `robo-nfe@1.4.2` |
96
96
  | `instanceId` | `string` | **obrigatório** | identidade única da instância — heartbeat do Monitor e agente do Manager convergem nela |
@@ -203,7 +203,7 @@ Bate "estou vivo" periodicamente; o servidor acusa o robô **offline** quando os
203
203
 
204
204
  ```js
205
205
  Hobots.init({
206
- dsn,
206
+ clientSecret,
207
207
  heartbeat: { intervalMs: 30_000, name: 'Robô NFe — VM 01' },
208
208
  });
209
209
  ```
@@ -223,13 +223,13 @@ Chame `close` antes do processo sair. Os timers do Monitor são `unref`'d — el
223
223
 
224
224
  ## Manager
225
225
 
226
- Capability de **execução remota sob demanda**, habilitada com `manager: true` (ou opções) no `init()`. O agente faz polling no app, executa os handlers registrados quando aparece trabalho e reporta status e logs incrementais. Mesma DSN, mesmo `debug` e o `instanceId` como identidade — nenhum handler global extra é instalado.
226
+ Capability de **execução remota sob demanda**, habilitada com `manager: true` (ou opções) no `init()`. O agente faz polling no app, executa os handlers registrados quando aparece trabalho e reporta status e logs incrementais. Mesmo Client Secret, mesmo `debug` e o `instanceId` como identidade — nenhum handler global extra é instalado.
227
227
 
228
228
  ### Ciclo de vida
229
229
 
230
230
  ```js
231
231
  Hobots.init({
232
- dsn: 'https://<secretKey>@<host>/<projectSlug>',
232
+ clientSecret: 'hb_<64 hex>',
233
233
  instanceId: 'vm-cliente-01',
234
234
  manager: true, // ou { pollIntervalMs, concurrency, ... }
235
235
  });
@@ -260,7 +260,7 @@ process.on('SIGINT', () => {
260
260
  | `taskTimeoutMs` | `number` | — | timeout client-side por run (não mata o handler; servidor é o backstop) |
261
261
  | `logFlushIntervalMs` | `number` | `2000` | intervalo do envio incremental de logs |
262
262
 
263
- A DSN, o `debug` e a identidade do agente (`instanceId`) vêm do `init()` — o heartbeat do Monitor e o agente do Manager convergem em **um agente** no painel automaticamente.
263
+ O Client Secret, o `debug` e a identidade do agente (`instanceId`) vêm do `init()` — o heartbeat do Monitor e o agente do Manager convergem em **um agente** no painel automaticamente.
264
264
 
265
265
  ### O handler e o `RunContext`
266
266
 
@@ -354,7 +354,7 @@ A 2.0 unifica o Manager no import raiz — `hobots/manager` **foi removido**:
354
354
 
355
355
  -Monitor.init({ dsn, serverName: 'vm-01', heartbeat: true });
356
356
  -Manager.init({ dsn, agentName: 'vm-01' });
357
- +Hobots.init({ dsn, instanceId: 'vm-01', heartbeat: true, manager: true });
357
+ +Hobots.init({ clientSecret, instanceId: 'vm-01', heartbeat: true, manager: true });
358
358
 
359
359
  -Manager.register('minha-task', handler);
360
360
  -Manager.start();
@@ -366,8 +366,9 @@ A 2.0 unifica o Manager no import raiz — `hobots/manager` **foi removido**:
366
366
  +await Hobots.close(15_000);
367
367
  ```
368
368
 
369
+ - A `dsn` (mecanismo antigo, `https://<secretKey>@host/<slug>`) não existe mais — passe `clientSecret` (em desenvolvimento, aponte para outro host com a env `HOBOTS_API_URL`). Copie o Client Secret em **Configurações → Projetos**.
369
370
  - `agentName` e `serverName` não existem mais — a identidade do agente é o `instanceId` (obrigatório, sem fallback).
370
- - As opções do Manager (`pollIntervalMs`, `concurrency`, `taskTimeoutMs`, `logFlushIntervalMs`) vão no campo `manager` do `init()`; `dsn` e `debug` não se repetem.
371
+ - As opções do Manager (`pollIntervalMs`, `concurrency`, `taskTimeoutMs`, `logFlushIntervalMs`) vão no campo `manager` do `init()`; `clientSecret` e `debug` não se repetem.
371
372
  - Tipos (`ManagerTaskHandler`, `RunContext`, `ManagerOptions`) e classes (`ManagerAgent`, `ManagerApi`) agora vêm de `hobots`; o wire format vem de `hobots/protocol`.
372
373
 
373
374
  ---
@@ -376,11 +377,11 @@ A 2.0 unifica o Manager no import raiz — `hobots/manager` **foi removido**:
376
377
 
377
378
  Guias detalhados acompanham o pacote em [`docs/`](./docs/README.md):
378
379
 
379
- - [Getting started](./docs/getting-started.md) — instalação, DSN, primeiro `init`, Monitor vs Hobots.
380
+ - [Getting started](./docs/getting-started.md) — instalação, Client Secret, primeiro `init`, Monitor vs Hobots.
380
381
  - [Monitor](./docs/monitor.md) — erros, breadcrumbs, escopo, transactions/spans, crons, heartbeat.
381
382
  - [Manager](./docs/manager.md) — handlers, `RunContext`, cancelamento, concorrência, idempotência.
382
383
  - [Referência de API](./docs/api-reference.md) — todas as funções e tabelas de opções com defaults.
383
- - [Protocolo & transporte](./docs/protocol.md) — a DSN única, endpoints, envelope, retries.
384
+ - [Protocolo & transporte](./docs/protocol.md) — o Client Secret, endpoints, envelope, retries.
384
385
 
385
386
  ---
386
387
 
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Client: () => Client,
24
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
24
25
  ManagerAgent: () => ManagerAgent,
25
26
  ManagerApi: () => ManagerApi,
26
27
  ManagerApiError: () => ManagerApiError,
@@ -35,8 +36,8 @@ __export(src_exports, {
35
36
  flush: () => flush,
36
37
  getClient: () => getClient,
37
38
  init: () => init,
38
- parseDsn: () => parseDsn,
39
39
  register: () => register,
40
+ resolveConfig: () => resolveConfig,
40
41
  setContext: () => setContext,
41
42
  setExtra: () => setExtra,
42
43
  setTag: () => setTag,
@@ -50,29 +51,28 @@ __export(src_exports, {
50
51
  });
51
52
  module.exports = __toCommonJS(src_exports);
52
53
 
53
- // src/dsn.ts
54
- function parseDsnUrl(dsn, prefix) {
55
- let url;
54
+ // src/config.ts
55
+ var DEFAULT_BASE_URL = "https://api.hobots.app";
56
+ function envBaseUrl() {
57
+ if (typeof process === "undefined" || !process.env) return void 0;
58
+ return process.env.HOBOTS_API_URL?.trim() || void 0;
59
+ }
60
+ function resolveConfig(options, prefix = "[hobots]") {
61
+ const key = options.clientSecret?.trim();
62
+ if (!key) {
63
+ throw new Error(`${prefix} clientSecret ausente \u2014 copie o Client Secret do projeto em Configura\xE7\xF5es \u2192 Projetos`);
64
+ }
65
+ const baseUrl = (envBaseUrl() ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
56
66
  try {
57
- url = new URL(dsn);
67
+ new URL(baseUrl);
58
68
  } catch {
59
- throw new Error(`${prefix} DSN inv\xE1lido: "${dsn}"`);
69
+ throw new Error(`${prefix} HOBOTS_API_URL inv\xE1lida: "${baseUrl}" \u2014 esperado ex.: http://localhost:3001`);
60
70
  }
61
- const key = url.username;
62
- const path = url.pathname.replace(/^\/+/, "");
63
- if (!key || !path) {
64
- throw new Error(`${prefix} DSN inv\xE1lido: "${dsn}" \u2014 esperado https://<key>@host/<projeto>`);
65
- }
66
- return { key, path, origin: `${url.protocol}//${url.host}` };
67
- }
68
- function parseDsn(dsn) {
69
- const { key, path, origin } = parseDsnUrl(dsn, "[hobots]");
70
71
  return {
71
72
  key,
72
- projectSlug: path,
73
- origin,
74
- ingestEndpoint: `${origin}/api/ingest`,
75
- managerBaseUrl: `${origin}/api/manager`
73
+ baseUrl,
74
+ ingestEndpoint: `${baseUrl}/api/ingest`,
75
+ managerBaseUrl: `${baseUrl}/api/manager`
76
76
  };
77
77
  }
78
78
 
@@ -308,16 +308,16 @@ function runtimeContexts() {
308
308
  }
309
309
  var Client = class {
310
310
  options;
311
- dsn;
311
+ config;
312
312
  logger;
313
313
  transport;
314
314
  constructor(options) {
315
315
  this.options = options;
316
316
  this.logger = makeLogger(!!options.debug);
317
- this.dsn = parseDsn(options.dsn);
317
+ this.config = resolveConfig(options);
318
318
  this.transport = new Transport({
319
- endpoint: this.dsn.ingestEndpoint,
320
- key: this.dsn.key,
319
+ endpoint: this.config.ingestEndpoint,
320
+ key: this.config.key,
321
321
  logger: this.logger,
322
322
  flushIntervalMs: options.flushIntervalMs,
323
323
  maxBatchSize: options.maxBatchSize
@@ -891,14 +891,14 @@ var ManagerAgent = class {
891
891
  this.options = options;
892
892
  this.registry = registry2;
893
893
  this.logger = makeLogger(options.debug ?? false, "[hobots-manager]");
894
- const dsn = parseDsn(options.dsn);
895
- this.api = new ManagerApi({ baseUrl: dsn.managerBaseUrl, key: dsn.key, logger: this.logger });
894
+ const config = resolveConfig(options, "[hobots-manager]");
895
+ this.api = new ManagerApi({ baseUrl: config.managerBaseUrl, key: config.key, logger: this.logger });
896
896
  this.pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
897
897
  this.concurrency = Math.min(MAX_CONCURRENCY, Math.max(1, options.concurrency ?? 1));
898
898
  this.instanceId = options.instanceId;
899
899
  this.taskTimeoutMs = options.taskTimeoutMs;
900
900
  this.logFlushIntervalMs = options.logFlushIntervalMs;
901
- this.logger.debug("agente configurado", { baseUrl: dsn.managerBaseUrl, projectSlug: dsn.projectSlug });
901
+ this.logger.debug("agente configurado", { baseUrl: config.managerBaseUrl });
902
902
  }
903
903
  logger;
904
904
  api;
@@ -995,7 +995,7 @@ var ManagerAgent = class {
995
995
  if (error instanceof ManagerApiError && error.status === 401) {
996
996
  this.consecutiveAuthFailures++;
997
997
  if (this.consecutiveAuthFailures === 1 || this.consecutiveAuthFailures % 10 === 0) {
998
- this.logger.error("chave rejeitada (401) \u2014 verifique a DSN do projeto em Configura\xE7\xF5es \u2192 Agentes");
998
+ this.logger.error("chave rejeitada (401) \u2014 verifique o Client Secret do projeto em Configura\xE7\xF5es \u2192 Projetos");
999
999
  }
1000
1000
  return Math.max(this.pollIntervalMs, AUTH_FAILURE_INTERVAL_MS);
1001
1001
  }
@@ -1109,11 +1109,11 @@ function init(options) {
1109
1109
  if (options.manager) {
1110
1110
  const managerOptions = options.manager === true ? {} : options.manager;
1111
1111
  agent = new ManagerAgent(
1112
- { ...managerOptions, dsn: options.dsn, instanceId: options.instanceId, debug: options.debug },
1112
+ { ...managerOptions, clientSecret: options.clientSecret, instanceId: options.instanceId, debug: options.debug },
1113
1113
  registry
1114
1114
  );
1115
1115
  }
1116
- client.logger.debug("inicializado", { endpoint: client.dsn.ingestEndpoint });
1116
+ client.logger.debug("inicializado", { endpoint: client.config.ingestEndpoint });
1117
1117
  }
1118
1118
  function getClient() {
1119
1119
  return client;