hobots 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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,14 @@ 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
+ // baseUrl: 'http://localhost:3002', // só em desenvolvimento
45
46
  instanceId: 'vm-cliente-01',
46
47
  heartbeat: true, // Monitor: "estou vivo" a cada 30s
47
48
  manager: { pollIntervalMs: 5_000 }, // Manager: execução sob demanda
@@ -57,15 +58,15 @@ Hobots.start(); // inicia o polling do Manager (segura o processo vivo)
57
58
 
58
59
  ---
59
60
 
60
- ## O DSN
61
+ ## O Client Secret
61
62
 
62
- Cada projeto tem **uma única DSN** — a mesma credencial autentica o Monitor **e** o Manager:
63
+ Cada projeto tem **um único Client Secret** — a mesma credencial autentica o Monitor **e** o Manager:
63
64
 
64
65
  ```
65
- https://<secretKey>@<host>/<projectSlug>
66
+ hb_<64 hex>
66
67
  ```
67
68
 
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.
69
+ (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 `https://app.hobots.app` por padrão; só sobrescreva `baseUrl` em desenvolvimento (ex.: `http://localhost:3002`).
69
70
 
70
71
  ---
71
72
 
@@ -79,7 +80,8 @@ Inicialize **uma vez**, o mais cedo possível no processo. Cria o cliente, insta
79
80
 
80
81
  ```js
81
82
  Hobots.init({
82
- dsn: 'https://<secretKey>@<host>/<projectSlug>',
83
+ clientSecret: 'hb_<64 hex>',
84
+ // baseUrl: 'http://localhost:3002', // só em desenvolvimento
83
85
  environment: 'production',
84
86
  release: 'robo-nfe@1.4.2',
85
87
  instanceId: 'vm-cliente-01',
@@ -90,7 +92,8 @@ Hobots.init({
90
92
 
91
93
  | Opção | Tipo | Default | Descrição |
92
94
  | --- | --- | --- | --- |
93
- | `dsn` | `string` | **obrigatório** | a DSN única do projeto: `https://<secretKey>@host/<projectSlug>` |
95
+ | `clientSecret` | `string` | **obrigatório** | o Client Secret do projeto (`hb_<64 hex>`), copiado em **Configurações → Projetos** |
96
+ | `baseUrl` | `string` | `'https://app.hobots.app'` | host do app da Hobots — só sobrescreva em desenvolvimento (ex.: `http://localhost:3002`) |
94
97
  | `environment` | `string` | `'production'` | ambiente lógico |
95
98
  | `release` | `string` | — | versão do programa, ex. `robo-nfe@1.4.2` |
96
99
  | `instanceId` | `string` | **obrigatório** | identidade única da instância — heartbeat do Monitor e agente do Manager convergem nela |
@@ -203,7 +206,7 @@ Bate "estou vivo" periodicamente; o servidor acusa o robô **offline** quando os
203
206
 
204
207
  ```js
205
208
  Hobots.init({
206
- dsn,
209
+ clientSecret,
207
210
  heartbeat: { intervalMs: 30_000, name: 'Robô NFe — VM 01' },
208
211
  });
209
212
  ```
@@ -223,13 +226,14 @@ Chame `close` antes do processo sair. Os timers do Monitor são `unref`'d — el
223
226
 
224
227
  ## Manager
225
228
 
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.
229
+ 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
230
 
228
231
  ### Ciclo de vida
229
232
 
230
233
  ```js
231
234
  Hobots.init({
232
- dsn: 'https://<secretKey>@<host>/<projectSlug>',
235
+ clientSecret: 'hb_<64 hex>',
236
+ // baseUrl: 'http://localhost:3002', // só em desenvolvimento
233
237
  instanceId: 'vm-cliente-01',
234
238
  manager: true, // ou { pollIntervalMs, concurrency, ... }
235
239
  });
@@ -260,7 +264,7 @@ process.on('SIGINT', () => {
260
264
  | `taskTimeoutMs` | `number` | — | timeout client-side por run (não mata o handler; servidor é o backstop) |
261
265
  | `logFlushIntervalMs` | `number` | `2000` | intervalo do envio incremental de logs |
262
266
 
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.
267
+ 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
268
 
265
269
  ### O handler e o `RunContext`
266
270
 
@@ -354,7 +358,7 @@ A 2.0 unifica o Manager no import raiz — `hobots/manager` **foi removido**:
354
358
 
355
359
  -Monitor.init({ dsn, serverName: 'vm-01', heartbeat: true });
356
360
  -Manager.init({ dsn, agentName: 'vm-01' });
357
- +Hobots.init({ dsn, instanceId: 'vm-01', heartbeat: true, manager: true });
361
+ +Hobots.init({ clientSecret, instanceId: 'vm-01', heartbeat: true, manager: true });
358
362
 
359
363
  -Manager.register('minha-task', handler);
360
364
  -Manager.start();
@@ -366,8 +370,9 @@ A 2.0 unifica o Manager no import raiz — `hobots/manager` **foi removido**:
366
370
  +await Hobots.close(15_000);
367
371
  ```
368
372
 
373
+ - A `dsn` (mecanismo antigo, `https://<secretKey>@host/<slug>`) não existe mais — passe `clientSecret` (e `baseUrl` só em desenvolvimento). Copie o Client Secret em **Configurações → Projetos**.
369
374
  - `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.
375
+ - As opções do Manager (`pollIntervalMs`, `concurrency`, `taskTimeoutMs`, `logFlushIntervalMs`) vão no campo `manager` do `init()`; `clientSecret` e `debug` não se repetem.
371
376
  - Tipos (`ManagerTaskHandler`, `RunContext`, `ManagerOptions`) e classes (`ManagerAgent`, `ManagerApi`) agora vêm de `hobots`; o wire format vem de `hobots/protocol`.
372
377
 
373
378
  ---
@@ -376,11 +381,11 @@ A 2.0 unifica o Manager no import raiz — `hobots/manager` **foi removido**:
376
381
 
377
382
  Guias detalhados acompanham o pacote em [`docs/`](./docs/README.md):
378
383
 
379
- - [Getting started](./docs/getting-started.md) — instalação, DSN, primeiro `init`, Monitor vs Hobots.
384
+ - [Getting started](./docs/getting-started.md) — instalação, Client Secret, primeiro `init`, Monitor vs Hobots.
380
385
  - [Monitor](./docs/monitor.md) — erros, breadcrumbs, escopo, transactions/spans, crons, heartbeat.
381
386
  - [Manager](./docs/manager.md) — handlers, `RunContext`, cancelamento, concorrência, idempotência.
382
387
  - [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.
388
+ - [Protocolo & transporte](./docs/protocol.md) — o Client Secret, endpoints, envelope, retries.
384
389
 
385
390
  ---
386
391
 
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,24 @@ __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://app.hobots.app";
56
+ function resolveConfig(options, prefix = "[hobots]") {
57
+ const key = options.clientSecret?.trim();
58
+ if (!key) {
59
+ throw new Error(`${prefix} clientSecret ausente \u2014 copie o Client Secret do projeto em Configura\xE7\xF5es \u2192 Projetos`);
60
+ }
61
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
56
62
  try {
57
- url = new URL(dsn);
63
+ new URL(baseUrl);
58
64
  } catch {
59
- throw new Error(`${prefix} DSN inv\xE1lido: "${dsn}"`);
65
+ throw new Error(`${prefix} baseUrl inv\xE1lida: "${baseUrl}" \u2014 esperado ex.: https://app.hobots.app`);
60
66
  }
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
67
  return {
71
68
  key,
72
- projectSlug: path,
73
- origin,
74
- ingestEndpoint: `${origin}/api/ingest`,
75
- managerBaseUrl: `${origin}/api/manager`
69
+ baseUrl,
70
+ ingestEndpoint: `${baseUrl}/api/ingest`,
71
+ managerBaseUrl: `${baseUrl}/api/manager`
76
72
  };
77
73
  }
78
74
 
@@ -308,16 +304,16 @@ function runtimeContexts() {
308
304
  }
309
305
  var Client = class {
310
306
  options;
311
- dsn;
307
+ config;
312
308
  logger;
313
309
  transport;
314
310
  constructor(options) {
315
311
  this.options = options;
316
312
  this.logger = makeLogger(!!options.debug);
317
- this.dsn = parseDsn(options.dsn);
313
+ this.config = resolveConfig(options);
318
314
  this.transport = new Transport({
319
- endpoint: this.dsn.ingestEndpoint,
320
- key: this.dsn.key,
315
+ endpoint: this.config.ingestEndpoint,
316
+ key: this.config.key,
321
317
  logger: this.logger,
322
318
  flushIntervalMs: options.flushIntervalMs,
323
319
  maxBatchSize: options.maxBatchSize
@@ -891,14 +887,14 @@ var ManagerAgent = class {
891
887
  this.options = options;
892
888
  this.registry = registry2;
893
889
  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 });
890
+ const config = resolveConfig(options, "[hobots-manager]");
891
+ this.api = new ManagerApi({ baseUrl: config.managerBaseUrl, key: config.key, logger: this.logger });
896
892
  this.pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
897
893
  this.concurrency = Math.min(MAX_CONCURRENCY, Math.max(1, options.concurrency ?? 1));
898
894
  this.instanceId = options.instanceId;
899
895
  this.taskTimeoutMs = options.taskTimeoutMs;
900
896
  this.logFlushIntervalMs = options.logFlushIntervalMs;
901
- this.logger.debug("agente configurado", { baseUrl: dsn.managerBaseUrl, projectSlug: dsn.projectSlug });
897
+ this.logger.debug("agente configurado", { baseUrl: config.managerBaseUrl });
902
898
  }
903
899
  logger;
904
900
  api;
@@ -995,7 +991,7 @@ var ManagerAgent = class {
995
991
  if (error instanceof ManagerApiError && error.status === 401) {
996
992
  this.consecutiveAuthFailures++;
997
993
  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");
994
+ this.logger.error("chave rejeitada (401) \u2014 verifique o Client Secret do projeto em Configura\xE7\xF5es \u2192 Projetos");
999
995
  }
1000
996
  return Math.max(this.pollIntervalMs, AUTH_FAILURE_INTERVAL_MS);
1001
997
  }
@@ -1109,11 +1105,11 @@ function init(options) {
1109
1105
  if (options.manager) {
1110
1106
  const managerOptions = options.manager === true ? {} : options.manager;
1111
1107
  agent = new ManagerAgent(
1112
- { ...managerOptions, dsn: options.dsn, instanceId: options.instanceId, debug: options.debug },
1108
+ { ...managerOptions, clientSecret: options.clientSecret, baseUrl: options.baseUrl, instanceId: options.instanceId, debug: options.debug },
1113
1109
  registry
1114
1110
  );
1115
1111
  }
1116
- client.logger.debug("inicializado", { endpoint: client.dsn.ingestEndpoint });
1112
+ client.logger.debug("inicializado", { endpoint: client.config.ingestEndpoint });
1117
1113
  }
1118
1114
  function getClient() {
1119
1115
  return client;