@diegosouzacdv/jev-browser-mcp 0.6.0 → 0.6.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
@@ -17,7 +17,7 @@ versão em produção, use o número explícito no argumento do pacote:
17
17
  "mcpServers": {
18
18
  "jev-browser": {
19
19
  "command": "npx",
20
- "args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.6.0"],
20
+ "args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.6.1"],
21
21
  "env": {
22
22
  "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
23
23
  "JEV_BROWSER_MODE": "computer"
@@ -32,7 +32,7 @@ um secret manager ou pelo ambiente do processo; não grave a chave no arquivo
32
32
  de configuração. Para instalar no projeto Node do próprio harness:
33
33
 
34
34
  ```sh
35
- npm install @diegosouzacdv/jev-browser-mcp
35
+ npm install @diegosouzacdv/jev-browser-mcp@0.6.1
36
36
  ```
37
37
 
38
38
  Se o harness executa o MCP repetidamente nesta máquina, instale uma versão
@@ -40,7 +40,7 @@ fixa globalmente para evitar a resolução e o download feitos pelo `npx` em cad
40
40
  inicialização:
41
41
 
42
42
  ```sh
43
- npm install --global @diegosouzacdv/jev-browser-mcp@0.6.0
43
+ npm install --global @diegosouzacdv/jev-browser-mcp@0.6.1
44
44
  ```
45
45
 
46
46
  Depois configure o servidor MCP com `command: "jev-browser-mcp"` e `args: []`.
@@ -49,7 +49,7 @@ Para atualizar, rode `npm install --global
49
49
 
50
50
  O padrão `computer` exige Chrome ou Edge instalado. Se optar por `harness`,
51
51
  configure `JEV_BROWSER_MODE=harness` antes de instalar o navegador gerenciado
52
- pelo Playwright com `npx --yes @diegosouzacdv/jev-browser-mcp --install-browser`.
52
+ pelo Playwright com `npx --yes @diegosouzacdv/jev-browser-mcp@0.6.1 --install-browser`.
53
53
 
54
54
  O padrão é `computer`: o pacote abre o Chrome/Edge instalado e usa um perfil
55
55
  persistente exclusivo em `browser.computer_user_data_dir`. No modo `harness`, a
@@ -92,7 +92,13 @@ Playwright. O transporte MCP usa `stdio`; toda saída de diagnóstico vai para
92
92
  O modo `computer` grava cookies no perfil exclusivo configurado, e conteúdo da
93
93
  página pode conter instruções maliciosas. O Jev recebe a captura acessível com
94
94
  uma instrução para tratar esse conteúdo como dado não confiável; não inclua
95
- segredos no fluxo, no resultado esperado ou nas descrições dos planos.
95
+ segredos no fluxo, no resultado esperado ou nas descrições dos planos. Antes de
96
+ enviar contexto ao provedor, o cliente mascara valores de campos e padrões
97
+ detectados de CPF/CNPJ, email, telefone, nome de cliente e valores monetários.
98
+ Isso reduz exposição acidental, mas não substitui o cuidado com os dados que o
99
+ harness escolhe incluir no fluxo. Use `local_only: true` quando nenhuma chamada
100
+ externa ao Jev puder ocorrer; esse modo recusa fluxos que precisam escolher
101
+ entre vários planos.
96
102
 
97
103
  Para um fluxo conhecido, o LLM do harness procura o cenário e os critérios de
98
104
  aceitação no projeto e chama `jev_browser.run_browser_flow` com planos
@@ -148,17 +154,24 @@ Cada plano pode usar:
148
154
 
149
155
  As proteções e evidências por etapa usam estes campos:
150
156
 
151
- - `confirm_dialog` recebe `expected_text` e `button`. O MCP exige um diálogo
152
- visível único e confere se ele contém o texto esperado antes de clicar; se o
153
- alerta mudou, a etapa falha sem clicar no botão. Reserve `click` comum para
154
- ações que não dependem do conteúdo de uma confirmação;
157
+ - `confirm_dialog` recebe `expected_text` e `button`. Em diálogos HTML, o MCP
158
+ confere o texto antes de localizar e clicar no botão. Também trata diálogos
159
+ nativos `alert`/`confirm`: precisa haver uma etapa `confirm_dialog` logo após
160
+ o clique que os abre, o texto deve corresponder e um diálogo inesperado é
161
+ fechado sem aceitar;
155
162
  - `mutating: true` marca etapas que gravam dados ou disparam efeitos externos.
156
- Com `options.dry_run: true`, o fluxo para imediatamente antes da primeira
157
- etapa marcada e retorna `dry_run_stopped_before_step` e `mutating_steps`.
158
- Marque toda ação que grava ou envia algo, mesmo quando não for um botão
159
- chamado Salvar;
163
+ O MCP também classifica botões conhecidos como `Salvar`, `Emitir`, `Cancelar`
164
+ ou `Sim` pelo nome e pelo controle resolvido, inclusive quando o plano usa um
165
+ seletor CSS. `options.dry_run: true` executa até a primeira etapa mutável e
166
+ para antes dela; retorna `dry_run_stopped_before_step` e um
167
+ `confirmation_token` temporário, de uso único e vinculado ao fluxo e à página.
168
+ Reenvie a mesma chamada com `options.confirmation_token` para autorizar essa
169
+ etapa. O MCP pausa novamente antes de cada outra etapa mutável. Sem `dry_run`,
170
+ o primeiro pedido de ação mutável também retorna `status: "confirmation_required"`
171
+ e token; nenhuma etapa mutável roda sem essa autorização. O token expira após
172
+ dez minutos por padrão;
160
173
  - `duration_ms` aparece em cada etapa concluída ou falha. `mutating_steps`
161
- lista as etapas marcadas e informa quais foram executadas;
174
+ lista etapas mutáveis que foram executadas ou falharam;
162
175
  - `screenshot: true` salva uma captura depois da etapa e inclui seu caminho na
163
176
  evidência da etapa;
164
177
  - `options.report_path` grava um resumo `.md` ou JUnit `.xml`. O caminho deve
@@ -210,6 +223,17 @@ ocorrência zero-based dentro desse escopo; sem `index`, o MCP exige exatamente
210
223
  um alvo. Para uma página com rótulo `for` quebrado, `label` também procura o
211
224
  controle próximo ao texto visível do rótulo.
212
225
 
226
+ `within` pode combinar um container e uma linha. Use, por exemplo,
227
+ `{"role":"cell","name":"Documento A","within":{"role":"table","row_containing_word":"1528721"}}`.
228
+ `row_containing_word` usa limites de palavra para não confundir `1528721` com
229
+ `15287210`; `row_containing_exact` continua disponível para texto de célula
230
+ exato. `check` e `uncheck` alteram checkboxes, e `select_option` aceita rótulo
231
+ exato (`option`), valor (`value`) ou rótulo parcial único (`label_contains`).
232
+ `navigate_menu` percorre uma lista de rótulos exatos e abre `#open_btn` quando o
233
+ primeiro item ainda está oculto. Se vários itens visíveis tiverem o mesmo
234
+ rótulo, seleciona a última ocorrência, útil quando o submenu recém-aberto
235
+ repete o nome da categoria pai.
236
+
213
237
  ```json
214
238
  {
215
239
  "action": "click",
@@ -429,11 +453,13 @@ limita quantas descrições de violações axe entram no resultado; a contagem t
429
453
  continua informada mesmo quando a lista é truncada.
430
454
 
431
455
  O `run_browser_flow` aceita `options` com `fast_path`, `snapshot_scope`,
432
- `block_trackers`, `capture_console_errors`, `capture_network_errors`,
433
- `capture_network_error_bodies`, `ready_timeout_seconds`, `ready_network_idle`,
434
- `ready_stable_ms`, `ready_text`, `continue_from_current_page`, `reuse_page`, `screenshot_on_failure`,
435
- `trace_on_failure`, `snapshot_include_hidden`, `stop_on_expected`, `dry_run` e
436
- `report_path`. Sem override,
456
+ `block_trackers`, `block_fonts`, `local_only`, `busy_selectors`,
457
+ `auto_angular_idle`, `login_url_contains`, `login_text`, `capture_console_errors`,
458
+ `capture_network_errors`, `capture_network_error_bodies`, `ready_timeout_seconds`,
459
+ `ready_network_idle`, `ready_stable_ms`, `ready_text`, `continue_from_current_page`,
460
+ `reuse_page`, `step_timeout_seconds`, `max_flow_steps`, `return_snapshot`,
461
+ `screenshot_on_failure`, `trace_on_failure`, `snapshot_include_hidden`,
462
+ `stop_on_expected`, `dry_run`, `confirmation_token` e `report_path`. Sem override,
437
463
  os padrões são lidos de `jev_browser_mcp` em
438
464
  `config/ui-testing.json`: um plano candidato pula a chamada Decisions;
439
465
  captura de console e screenshot de falha ficam ligadas; trace, rede e bloqueio
@@ -446,12 +472,22 @@ sem esperar novamente por `load`/`networkidle`. As etapas continuam aguardando
446
472
  seus próprios alvos e condições. `reuse_page` segue como alias legado; defina
447
473
  `continue_from_current_page: false` para iniciar cada chamada em `initial_url`.
448
474
  Se a página atual e `initial_url` tiverem origens diferentes, o MCP navega para
449
- `initial_url`. `snapshot_scope` aceita `body`, `main` ou `dialog`; se `main` não
450
- existir, o snapshot usa `body`.
451
-
452
- `block_trackers: true` bloqueia os domínios e tipos de recurso listados na
453
- configuração (analytics, Hotjar, fontes externas e mídia). Isso pode alterar o
454
- layout ou o comportamento do site, então a opção é desligada por padrão.
475
+ `initial_url`. Se forem da mesma origem, continua a página atual e avisa quando
476
+ as URLs completas forem diferentes. `snapshot_scope` aceita `body`, `main` ou
477
+ `dialog`; se `main` não existir, o snapshot usa `body`.
478
+
479
+ `block_trackers: true` bloqueia analytics/trackers e os tipos de recurso
480
+ configurados. Fontes ficam habilitadas por padrão para preservar ícones e
481
+ glyphs; `block_fonts: true` bloqueia fontes separadamente. `busy_selectors`
482
+ complementa `aria-busy="true"` ao aguardar overlays de carregamento.
483
+ `auto_angular_idle` aguarda AngularJS depois de cliques e digitação quando a
484
+ página expõe o injector. `login_url_contains` e `login_text` substituem a
485
+ detecção padrão de autenticação. `local_only: true` recusa qualquer etapa que
486
+ precisaria chamar o provedor Jev; use exatamente um plano com `fast_path: true`.
487
+ `step_timeout_seconds` define o timeout padrão por etapa e cada etapa pode
488
+ substituí-lo com `timeout_seconds`. `max_flow_steps` reduz o limite por chamada,
489
+ sem superar o teto da configuração. `return_snapshot` aceita `full`, `diff` ou
490
+ `none`; `diff` retorna linhas acessíveis novas desde o snapshot inicial.
455
491
 
456
492
  Com `capture_console_errors` e `capture_network_errors`, o retorno traz
457
493
  `console_errors` e `network_failures`, limitados em quantidade e tamanho.
@@ -482,7 +518,10 @@ Chrome/Edge; em outros sistemas, usa o `SingletonLock` do Chromium. Configure
482
518
  Resultados MCP incluem `server_version`; erros também começam com a versão do
483
519
  servidor para facilitar a comparação entre instalações. A ferramenta
484
520
  `browser_health` informa se a sessão está ativa e tenta reconectar um browser
485
- que encerrou desde a chamada anterior.
521
+ que encerrou desde a chamada anterior. Passe `{"restart":true}` para fechar e
522
+ reiniciar somente o navegador administrado por este processo MCP. Um fluxo pode
523
+ ser repetido automaticamente uma vez após `BROWSER_DISCONNECTED` quando nenhuma
524
+ etapa mutável foi executada.
486
525
 
487
526
  `harness_browser` e `computer_browser` aceitam `chrome` ou `msedge`.
488
527
  `computer_user_data_dir` deve ficar fora do repositório e conter `{browser}`;
@@ -23,6 +23,7 @@
23
23
  "jev_browser_mcp": {
24
24
  "browser": {
25
25
  "max_action_timeout_seconds": 8,
26
+ "mutation_confirmation_ttl_seconds": 600,
26
27
  "ready_timeout_seconds_default": 15,
27
28
  "max_ready_timeout_seconds": 60,
28
29
  "ready_network_idle_default": true,
@@ -56,6 +57,14 @@
56
57
  "capture_console_errors_default": true,
57
58
  "capture_network_errors_default": false,
58
59
  "block_trackers_default": false,
60
+ "block_fonts_default": false,
61
+ "local_only_default": false,
62
+ "auto_angular_idle_default": false,
63
+ "return_snapshot_default": "full",
64
+ "busy_selectors_default": [],
65
+ "login_url_contains_default": ["/auth"],
66
+ "login_text_default": [],
67
+ "max_busy_selectors": 8,
59
68
  "snapshot_scope_default": "body",
60
69
  "tracker_host_suffixes": [
61
70
  "google-analytics.com",
@@ -64,7 +73,7 @@
64
73
  "fonts.googleapis.com",
65
74
  "fonts.gstatic.com"
66
75
  ],
67
- "blocked_resource_types": ["font", "media"]
76
+ "blocked_resource_types": ["media"]
68
77
  },
69
78
  "jev": {
70
79
  "max_diagnostic_items": 50,
@@ -17,7 +17,7 @@ versão em produção, use o número explícito no argumento do pacote:
17
17
  "mcpServers": {
18
18
  "jev-browser": {
19
19
  "command": "npx",
20
- "args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.6.0"],
20
+ "args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.6.1"],
21
21
  "env": {
22
22
  "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
23
23
  "JEV_BROWSER_MODE": "computer"
@@ -32,7 +32,7 @@ um secret manager ou pelo ambiente do processo; não grave a chave no arquivo
32
32
  de configuração. Para instalar no projeto Node do próprio harness:
33
33
 
34
34
  ```sh
35
- npm install @diegosouzacdv/jev-browser-mcp
35
+ npm install @diegosouzacdv/jev-browser-mcp@0.6.1
36
36
  ```
37
37
 
38
38
  Se o harness executa o MCP repetidamente nesta máquina, instale uma versão
@@ -40,7 +40,7 @@ fixa globalmente para evitar a resolução e o download feitos pelo `npx` em cad
40
40
  inicialização:
41
41
 
42
42
  ```sh
43
- npm install --global @diegosouzacdv/jev-browser-mcp@0.6.0
43
+ npm install --global @diegosouzacdv/jev-browser-mcp@0.6.1
44
44
  ```
45
45
 
46
46
  Depois configure o servidor MCP com `command: "jev-browser-mcp"` e `args: []`.
@@ -49,7 +49,7 @@ Para atualizar, rode `npm install --global
49
49
 
50
50
  O padrão `computer` exige Chrome ou Edge instalado. Se optar por `harness`,
51
51
  configure `JEV_BROWSER_MODE=harness` antes de instalar o navegador gerenciado
52
- pelo Playwright com `npx --yes @diegosouzacdv/jev-browser-mcp --install-browser`.
52
+ pelo Playwright com `npx --yes @diegosouzacdv/jev-browser-mcp@0.6.1 --install-browser`.
53
53
 
54
54
  O padrão é `computer`: o pacote abre o Chrome/Edge instalado e usa um perfil
55
55
  persistente exclusivo em `browser.computer_user_data_dir`. No modo `harness`, a
@@ -92,7 +92,13 @@ Playwright. O transporte MCP usa `stdio`; toda saída de diagnóstico vai para
92
92
  O modo `computer` grava cookies no perfil exclusivo configurado, e conteúdo da
93
93
  página pode conter instruções maliciosas. O Jev recebe a captura acessível com
94
94
  uma instrução para tratar esse conteúdo como dado não confiável; não inclua
95
- segredos no fluxo, no resultado esperado ou nas descrições dos planos.
95
+ segredos no fluxo, no resultado esperado ou nas descrições dos planos. Antes de
96
+ enviar contexto ao provedor, o cliente mascara valores de campos e padrões
97
+ detectados de CPF/CNPJ, email, telefone, nome de cliente e valores monetários.
98
+ Isso reduz exposição acidental, mas não substitui o cuidado com os dados que o
99
+ harness escolhe incluir no fluxo. Use `local_only: true` quando nenhuma chamada
100
+ externa ao Jev puder ocorrer; esse modo recusa fluxos que precisam escolher
101
+ entre vários planos.
96
102
 
97
103
  Para um fluxo conhecido, o LLM do harness procura o cenário e os critérios de
98
104
  aceitação no projeto e chama `jev_browser.run_browser_flow` com planos
@@ -148,17 +154,24 @@ Cada plano pode usar:
148
154
 
149
155
  As proteções e evidências por etapa usam estes campos:
150
156
 
151
- - `confirm_dialog` recebe `expected_text` e `button`. O MCP exige um diálogo
152
- visível único e confere se ele contém o texto esperado antes de clicar; se o
153
- alerta mudou, a etapa falha sem clicar no botão. Reserve `click` comum para
154
- ações que não dependem do conteúdo de uma confirmação;
157
+ - `confirm_dialog` recebe `expected_text` e `button`. Em diálogos HTML, o MCP
158
+ confere o texto antes de localizar e clicar no botão. Também trata diálogos
159
+ nativos `alert`/`confirm`: precisa haver uma etapa `confirm_dialog` logo após
160
+ o clique que os abre, o texto deve corresponder e um diálogo inesperado é
161
+ fechado sem aceitar;
155
162
  - `mutating: true` marca etapas que gravam dados ou disparam efeitos externos.
156
- Com `options.dry_run: true`, o fluxo para imediatamente antes da primeira
157
- etapa marcada e retorna `dry_run_stopped_before_step` e `mutating_steps`.
158
- Marque toda ação que grava ou envia algo, mesmo quando não for um botão
159
- chamado Salvar;
163
+ O MCP também classifica botões conhecidos como `Salvar`, `Emitir`, `Cancelar`
164
+ ou `Sim` pelo nome e pelo controle resolvido, inclusive quando o plano usa um
165
+ seletor CSS. `options.dry_run: true` executa até a primeira etapa mutável e
166
+ para antes dela; retorna `dry_run_stopped_before_step` e um
167
+ `confirmation_token` temporário, de uso único e vinculado ao fluxo e à página.
168
+ Reenvie a mesma chamada com `options.confirmation_token` para autorizar essa
169
+ etapa. O MCP pausa novamente antes de cada outra etapa mutável. Sem `dry_run`,
170
+ o primeiro pedido de ação mutável também retorna `status: "confirmation_required"`
171
+ e token; nenhuma etapa mutável roda sem essa autorização. O token expira após
172
+ dez minutos por padrão;
160
173
  - `duration_ms` aparece em cada etapa concluída ou falha. `mutating_steps`
161
- lista as etapas marcadas e informa quais foram executadas;
174
+ lista etapas mutáveis que foram executadas ou falharam;
162
175
  - `screenshot: true` salva uma captura depois da etapa e inclui seu caminho na
163
176
  evidência da etapa;
164
177
  - `options.report_path` grava um resumo `.md` ou JUnit `.xml`. O caminho deve
@@ -210,6 +223,17 @@ ocorrência zero-based dentro desse escopo; sem `index`, o MCP exige exatamente
210
223
  um alvo. Para uma página com rótulo `for` quebrado, `label` também procura o
211
224
  controle próximo ao texto visível do rótulo.
212
225
 
226
+ `within` pode combinar um container e uma linha. Use, por exemplo,
227
+ `{"role":"cell","name":"Documento A","within":{"role":"table","row_containing_word":"1528721"}}`.
228
+ `row_containing_word` usa limites de palavra para não confundir `1528721` com
229
+ `15287210`; `row_containing_exact` continua disponível para texto de célula
230
+ exato. `check` e `uncheck` alteram checkboxes, e `select_option` aceita rótulo
231
+ exato (`option`), valor (`value`) ou rótulo parcial único (`label_contains`).
232
+ `navigate_menu` percorre uma lista de rótulos exatos e abre `#open_btn` quando o
233
+ primeiro item ainda está oculto. Se vários itens visíveis tiverem o mesmo
234
+ rótulo, seleciona a última ocorrência, útil quando o submenu recém-aberto
235
+ repete o nome da categoria pai.
236
+
213
237
  ```json
214
238
  {
215
239
  "action": "click",
@@ -429,11 +453,13 @@ limita quantas descrições de violações axe entram no resultado; a contagem t
429
453
  continua informada mesmo quando a lista é truncada.
430
454
 
431
455
  O `run_browser_flow` aceita `options` com `fast_path`, `snapshot_scope`,
432
- `block_trackers`, `capture_console_errors`, `capture_network_errors`,
433
- `capture_network_error_bodies`, `ready_timeout_seconds`, `ready_network_idle`,
434
- `ready_stable_ms`, `ready_text`, `continue_from_current_page`, `reuse_page`, `screenshot_on_failure`,
435
- `trace_on_failure`, `snapshot_include_hidden`, `stop_on_expected`, `dry_run` e
436
- `report_path`. Sem override,
456
+ `block_trackers`, `block_fonts`, `local_only`, `busy_selectors`,
457
+ `auto_angular_idle`, `login_url_contains`, `login_text`, `capture_console_errors`,
458
+ `capture_network_errors`, `capture_network_error_bodies`, `ready_timeout_seconds`,
459
+ `ready_network_idle`, `ready_stable_ms`, `ready_text`, `continue_from_current_page`,
460
+ `reuse_page`, `step_timeout_seconds`, `max_flow_steps`, `return_snapshot`,
461
+ `screenshot_on_failure`, `trace_on_failure`, `snapshot_include_hidden`,
462
+ `stop_on_expected`, `dry_run`, `confirmation_token` e `report_path`. Sem override,
437
463
  os padrões são lidos de `jev_browser_mcp` em
438
464
  `config/ui-testing.json`: um plano candidato pula a chamada Decisions;
439
465
  captura de console e screenshot de falha ficam ligadas; trace, rede e bloqueio
@@ -446,12 +472,22 @@ sem esperar novamente por `load`/`networkidle`. As etapas continuam aguardando
446
472
  seus próprios alvos e condições. `reuse_page` segue como alias legado; defina
447
473
  `continue_from_current_page: false` para iniciar cada chamada em `initial_url`.
448
474
  Se a página atual e `initial_url` tiverem origens diferentes, o MCP navega para
449
- `initial_url`. `snapshot_scope` aceita `body`, `main` ou `dialog`; se `main` não
450
- existir, o snapshot usa `body`.
451
-
452
- `block_trackers: true` bloqueia os domínios e tipos de recurso listados na
453
- configuração (analytics, Hotjar, fontes externas e mídia). Isso pode alterar o
454
- layout ou o comportamento do site, então a opção é desligada por padrão.
475
+ `initial_url`. Se forem da mesma origem, continua a página atual e avisa quando
476
+ as URLs completas forem diferentes. `snapshot_scope` aceita `body`, `main` ou
477
+ `dialog`; se `main` não existir, o snapshot usa `body`.
478
+
479
+ `block_trackers: true` bloqueia analytics/trackers e os tipos de recurso
480
+ configurados. Fontes ficam habilitadas por padrão para preservar ícones e
481
+ glyphs; `block_fonts: true` bloqueia fontes separadamente. `busy_selectors`
482
+ complementa `aria-busy="true"` ao aguardar overlays de carregamento.
483
+ `auto_angular_idle` aguarda AngularJS depois de cliques e digitação quando a
484
+ página expõe o injector. `login_url_contains` e `login_text` substituem a
485
+ detecção padrão de autenticação. `local_only: true` recusa qualquer etapa que
486
+ precisaria chamar o provedor Jev; use exatamente um plano com `fast_path: true`.
487
+ `step_timeout_seconds` define o timeout padrão por etapa e cada etapa pode
488
+ substituí-lo com `timeout_seconds`. `max_flow_steps` reduz o limite por chamada,
489
+ sem superar o teto da configuração. `return_snapshot` aceita `full`, `diff` ou
490
+ `none`; `diff` retorna linhas acessíveis novas desde o snapshot inicial.
455
491
 
456
492
  Com `capture_console_errors` e `capture_network_errors`, o retorno traz
457
493
  `console_errors` e `network_failures`, limitados em quantidade e tamanho.
@@ -482,7 +518,10 @@ Chrome/Edge; em outros sistemas, usa o `SingletonLock` do Chromium. Configure
482
518
  Resultados MCP incluem `server_version`; erros também começam com a versão do
483
519
  servidor para facilitar a comparação entre instalações. A ferramenta
484
520
  `browser_health` informa se a sessão está ativa e tenta reconectar um browser
485
- que encerrou desde a chamada anterior.
521
+ que encerrou desde a chamada anterior. Passe `{"restart":true}` para fechar e
522
+ reiniciar somente o navegador administrado por este processo MCP. Um fluxo pode
523
+ ser repetido automaticamente uma vez após `BROWSER_DISCONNECTED` quando nenhuma
524
+ etapa mutável foi executada.
486
525
 
487
526
  `harness_browser` e `computer_browser` aceitam `chrome` ou `msedge`.
488
527
  `computer_user_data_dir` deve ficar fora do repositório e conter `{browser}`;
@@ -16,23 +16,24 @@ const CONFIG_KEYS = {
16
16
  "max_flow_steps",
17
17
  "max_text_entry_chars",
18
18
  ],
19
- nodeBrowser: [
20
- "max_action_timeout_seconds",
21
- "ready_timeout_seconds_default",
22
- "max_ready_timeout_seconds",
23
- "ready_network_idle_default",
24
- "ready_stable_ms_default",
25
- "max_ready_stable_ms",
26
- "post_step_ready_timeout_seconds_default",
27
- "max_step_timeout_seconds",
28
- "key_delay_ms_default",
29
- "reuse_page_default",
30
- "stop_on_expected_default",
31
- "snapshot_include_hidden_default",
32
- "visibility_poll_interval_ms_default",
33
- "capture_network_error_bodies_default",
34
- "max_network_error_body_bytes",
35
- "max_network_error_message_chars",
19
+ nodeBrowser: [
20
+ "max_action_timeout_seconds",
21
+ "mutation_confirmation_ttl_seconds",
22
+ "ready_timeout_seconds_default",
23
+ "max_ready_timeout_seconds",
24
+ "ready_network_idle_default",
25
+ "ready_stable_ms_default",
26
+ "max_ready_stable_ms",
27
+ "post_step_ready_timeout_seconds_default",
28
+ "max_step_timeout_seconds",
29
+ "key_delay_ms_default",
30
+ "reuse_page_default",
31
+ "stop_on_expected_default",
32
+ "snapshot_include_hidden_default",
33
+ "visibility_poll_interval_ms_default",
34
+ "capture_network_error_bodies_default",
35
+ "max_network_error_body_bytes",
36
+ "max_network_error_message_chars",
36
37
  "max_upload_files",
37
38
  "max_upload_path_chars",
38
39
  "max_upload_file_bytes",
@@ -51,6 +52,14 @@ const CONFIG_KEYS = {
51
52
  "capture_console_errors_default",
52
53
  "capture_network_errors_default",
53
54
  "block_trackers_default",
55
+ "block_fonts_default",
56
+ "local_only_default",
57
+ "auto_angular_idle_default",
58
+ "return_snapshot_default",
59
+ "busy_selectors_default",
60
+ "login_url_contains_default",
61
+ "login_text_default",
62
+ "max_busy_selectors",
54
63
  "snapshot_scope_default",
55
64
  "tracker_host_suffixes",
56
65
  "blocked_resource_types",
@@ -221,7 +230,7 @@ export function loadSettings({ env = process.env, configPath = CONFIG_PATH } = {
221
230
  const uploadRoot = configuredUploadRoot
222
231
  ? configuredDirectory(configuredUploadRoot, `environment ${uploadRootEnv}`)
223
232
  : null;
224
- const snapshotScope = browserOptions.snapshot_scope_default;
233
+ const snapshotScope = browserOptions.snapshot_scope_default;
225
234
  if (!new Set(["body", "main", "dialog"]).has(snapshotScope)) {
226
235
  throw new JevBrowserError("config/ui-testing.json jev_browser_mcp.browser.snapshot_scope_default must be body, main, or dialog");
227
236
  }
@@ -230,28 +239,46 @@ export function loadSettings({ env = process.env, configPath = CONFIG_PATH } = {
230
239
  "jev_browser_mcp.browser.tracker_host_suffixes",
231
240
  (host) => /^[a-z0-9.-]+$/i.test(host) && !host.startsWith(".") && !host.endsWith("."),
232
241
  );
233
- const blockedResourceTypes = stringList(
242
+ const blockedResourceTypes = stringList(
234
243
  browserOptions.blocked_resource_types,
235
244
  "jev_browser_mcp.browser.blocked_resource_types",
236
245
  (type) => new Set(["font", "media", "image"]).has(type),
237
- );
238
- const maxReadyTimeoutSeconds = positiveNumber(browserOptions.max_ready_timeout_seconds, "jev_browser_mcp.browser.max_ready_timeout_seconds");
239
- const readyTimeoutSecondsDefault = positiveNumber(browserOptions.ready_timeout_seconds_default, "jev_browser_mcp.browser.ready_timeout_seconds_default");
240
- const maxReadyStableMs = positiveNumber(browserOptions.max_ready_stable_ms, "jev_browser_mcp.browser.max_ready_stable_ms", true);
241
- const readyStableMsDefault = positiveNumber(browserOptions.ready_stable_ms_default, "jev_browser_mcp.browser.ready_stable_ms_default", true);
242
- if (readyTimeoutSecondsDefault > maxReadyTimeoutSeconds) {
243
- throw new JevBrowserError("config/ui-testing.json ready timeout default exceeds its configured maximum");
244
- }
245
- if (readyStableMsDefault > maxReadyStableMs) {
246
- throw new JevBrowserError("config/ui-testing.json stable snapshot default exceeds its configured maximum");
247
- }
248
- const postStepReadyTimeoutSeconds = positiveNumber(
249
- browserOptions.post_step_ready_timeout_seconds_default,
250
- "jev_browser_mcp.browser.post_step_ready_timeout_seconds_default",
251
- );
252
- if (postStepReadyTimeoutSeconds > maxReadyTimeoutSeconds) {
253
- throw new JevBrowserError("config/ui-testing.json post-step readiness timeout exceeds its configured maximum");
254
- }
246
+ );
247
+ const busySelectorsDefault = stringList(
248
+ browserOptions.busy_selectors_default,
249
+ "jev_browser_mcp.browser.busy_selectors_default",
250
+ );
251
+ if (busySelectorsDefault.length > positiveNumber(browserOptions.max_busy_selectors, "jev_browser_mcp.browser.max_busy_selectors", true)) {
252
+ throw new JevBrowserError("config/ui-testing.json busy_selectors_default exceeds max_busy_selectors");
253
+ }
254
+ const loginUrlContainsDefault = stringList(
255
+ browserOptions.login_url_contains_default,
256
+ "jev_browser_mcp.browser.login_url_contains_default",
257
+ );
258
+ const loginTextDefault = stringList(
259
+ browserOptions.login_text_default,
260
+ "jev_browser_mcp.browser.login_text_default",
261
+ );
262
+ if (!new Set(["none", "diff", "full"]).has(browserOptions.return_snapshot_default)) {
263
+ throw new JevBrowserError("config/ui-testing.json return_snapshot_default must be none, diff, or full");
264
+ }
265
+ const maxReadyTimeoutSeconds = positiveNumber(browserOptions.max_ready_timeout_seconds, "jev_browser_mcp.browser.max_ready_timeout_seconds");
266
+ const readyTimeoutSecondsDefault = positiveNumber(browserOptions.ready_timeout_seconds_default, "jev_browser_mcp.browser.ready_timeout_seconds_default");
267
+ const maxReadyStableMs = positiveNumber(browserOptions.max_ready_stable_ms, "jev_browser_mcp.browser.max_ready_stable_ms", true);
268
+ const readyStableMsDefault = positiveNumber(browserOptions.ready_stable_ms_default, "jev_browser_mcp.browser.ready_stable_ms_default", true);
269
+ if (readyTimeoutSecondsDefault > maxReadyTimeoutSeconds) {
270
+ throw new JevBrowserError("config/ui-testing.json ready timeout default exceeds its configured maximum");
271
+ }
272
+ if (readyStableMsDefault > maxReadyStableMs) {
273
+ throw new JevBrowserError("config/ui-testing.json stable snapshot default exceeds its configured maximum");
274
+ }
275
+ const postStepReadyTimeoutSeconds = positiveNumber(
276
+ browserOptions.post_step_ready_timeout_seconds_default,
277
+ "jev_browser_mcp.browser.post_step_ready_timeout_seconds_default",
278
+ );
279
+ if (postStepReadyTimeoutSeconds > maxReadyTimeoutSeconds) {
280
+ throw new JevBrowserError("config/ui-testing.json post-step readiness timeout exceeds its configured maximum");
281
+ }
255
282
 
256
283
  return Object.freeze({
257
284
  env,
@@ -261,15 +288,16 @@ export function loadSettings({ env = process.env, configPath = CONFIG_PATH } = {
261
288
  profileDir: profileDirectory(profileTemplate, channel, env.JEV_BROWSER_PROFILE),
262
289
  maxFlowSteps: positiveNumber(document.browser.max_flow_steps, "browser.max_flow_steps", true),
263
290
  maxTextEntryChars: positiveNumber(document.browser.max_text_entry_chars, "browser.max_text_entry_chars", true),
264
- actionTimeoutMs: positiveNumber(browserOptions.max_action_timeout_seconds, "jev_browser_mcp.browser.max_action_timeout_seconds") * 1000,
265
- maxReadyTimeoutSeconds,
266
- maxReadyStableMs,
267
- postStepReadyTimeoutMs: postStepReadyTimeoutSeconds * 1000,
268
- maxStepTimeoutSeconds: positiveNumber(browserOptions.max_step_timeout_seconds, "jev_browser_mcp.browser.max_step_timeout_seconds"),
269
- keyDelayMs: positiveNumber(browserOptions.key_delay_ms_default, "jev_browser_mcp.browser.key_delay_ms_default", true),
270
- visibilityPollMs: positiveNumber(browserOptions.visibility_poll_interval_ms_default, "jev_browser_mcp.browser.visibility_poll_interval_ms_default", true),
271
- maxNetworkErrorBodyBytes: positiveNumber(browserOptions.max_network_error_body_bytes, "jev_browser_mcp.browser.max_network_error_body_bytes", true),
272
- maxNetworkErrorMessageChars: positiveNumber(browserOptions.max_network_error_message_chars, "jev_browser_mcp.browser.max_network_error_message_chars", true),
291
+ actionTimeoutMs: positiveNumber(browserOptions.max_action_timeout_seconds, "jev_browser_mcp.browser.max_action_timeout_seconds") * 1000,
292
+ mutationConfirmationTtlMs: positiveNumber(browserOptions.mutation_confirmation_ttl_seconds, "jev_browser_mcp.browser.mutation_confirmation_ttl_seconds") * 1000,
293
+ maxReadyTimeoutSeconds,
294
+ maxReadyStableMs,
295
+ postStepReadyTimeoutMs: postStepReadyTimeoutSeconds * 1000,
296
+ maxStepTimeoutSeconds: positiveNumber(browserOptions.max_step_timeout_seconds, "jev_browser_mcp.browser.max_step_timeout_seconds"),
297
+ keyDelayMs: positiveNumber(browserOptions.key_delay_ms_default, "jev_browser_mcp.browser.key_delay_ms_default", true),
298
+ visibilityPollMs: positiveNumber(browserOptions.visibility_poll_interval_ms_default, "jev_browser_mcp.browser.visibility_poll_interval_ms_default", true),
299
+ maxNetworkErrorBodyBytes: positiveNumber(browserOptions.max_network_error_body_bytes, "jev_browser_mcp.browser.max_network_error_body_bytes", true),
300
+ maxNetworkErrorMessageChars: positiveNumber(browserOptions.max_network_error_message_chars, "jev_browser_mcp.browser.max_network_error_message_chars", true),
273
301
  maxUploadFiles: positiveNumber(browserOptions.max_upload_files, "jev_browser_mcp.browser.max_upload_files", true),
274
302
  maxUploadPathChars: positiveNumber(browserOptions.max_upload_path_chars, "jev_browser_mcp.browser.max_upload_path_chars", true),
275
303
  maxUploadFileBytes: positiveNumber(browserOptions.max_upload_file_bytes, "jev_browser_mcp.browser.max_upload_file_bytes", true),
@@ -286,19 +314,27 @@ export function loadSettings({ env = process.env, configPath = CONFIG_PATH } = {
286
314
  screenshotOnFailure: booleanValue(browserOptions.screenshot_on_failure_default, "jev_browser_mcp.browser.screenshot_on_failure_default"),
287
315
  traceOnFailure: booleanValue(browserOptions.trace_on_failure_default, "jev_browser_mcp.browser.trace_on_failure_default"),
288
316
  captureConsoleErrors: booleanValue(browserOptions.capture_console_errors_default, "jev_browser_mcp.browser.capture_console_errors_default"),
289
- captureNetworkErrors: booleanValue(browserOptions.capture_network_errors_default, "jev_browser_mcp.browser.capture_network_errors_default"),
290
- captureNetworkErrorBodies: booleanValue(browserOptions.capture_network_error_bodies_default, "jev_browser_mcp.browser.capture_network_error_bodies_default"),
291
- blockTrackers: booleanValue(browserOptions.block_trackers_default, "jev_browser_mcp.browser.block_trackers_default"),
292
- readyNetworkIdle: booleanValue(browserOptions.ready_network_idle_default, "jev_browser_mcp.browser.ready_network_idle_default"),
293
- reusePage: booleanValue(browserOptions.reuse_page_default, "jev_browser_mcp.browser.reuse_page_default"),
294
- stopOnExpected: booleanValue(browserOptions.stop_on_expected_default, "jev_browser_mcp.browser.stop_on_expected_default"),
295
- snapshotIncludeHidden: booleanValue(browserOptions.snapshot_include_hidden_default, "jev_browser_mcp.browser.snapshot_include_hidden_default"),
296
- readyTimeoutSeconds: readyTimeoutSecondsDefault,
297
- readyStableMs: readyStableMsDefault,
317
+ captureNetworkErrors: booleanValue(browserOptions.capture_network_errors_default, "jev_browser_mcp.browser.capture_network_errors_default"),
318
+ captureNetworkErrorBodies: booleanValue(browserOptions.capture_network_error_bodies_default, "jev_browser_mcp.browser.capture_network_error_bodies_default"),
319
+ blockTrackers: booleanValue(browserOptions.block_trackers_default, "jev_browser_mcp.browser.block_trackers_default"),
320
+ blockFonts: booleanValue(browserOptions.block_fonts_default, "jev_browser_mcp.browser.block_fonts_default"),
321
+ localOnly: booleanValue(browserOptions.local_only_default, "jev_browser_mcp.browser.local_only_default"),
322
+ autoAngularIdle: booleanValue(browserOptions.auto_angular_idle_default, "jev_browser_mcp.browser.auto_angular_idle_default"),
323
+ returnSnapshot: browserOptions.return_snapshot_default,
324
+ readyNetworkIdle: booleanValue(browserOptions.ready_network_idle_default, "jev_browser_mcp.browser.ready_network_idle_default"),
325
+ reusePage: booleanValue(browserOptions.reuse_page_default, "jev_browser_mcp.browser.reuse_page_default"),
326
+ stopOnExpected: booleanValue(browserOptions.stop_on_expected_default, "jev_browser_mcp.browser.stop_on_expected_default"),
327
+ snapshotIncludeHidden: booleanValue(browserOptions.snapshot_include_hidden_default, "jev_browser_mcp.browser.snapshot_include_hidden_default"),
328
+ readyTimeoutSeconds: readyTimeoutSecondsDefault,
329
+ readyStableMs: readyStableMsDefault,
298
330
  snapshotScope,
299
331
  }),
300
332
  trackerHostSuffixes: Object.freeze(trackerHostSuffixes),
301
333
  blockedResourceTypes: Object.freeze(blockedResourceTypes),
334
+ busySelectorsDefault: Object.freeze(busySelectorsDefault),
335
+ loginUrlContainsDefault: Object.freeze(loginUrlContainsDefault),
336
+ loginTextDefault: Object.freeze(loginTextDefault),
337
+ maxBusySelectors: positiveNumber(browserOptions.max_busy_selectors, "jev_browser_mcp.browser.max_busy_selectors", true),
302
338
  }),
303
339
  jev: Object.freeze({
304
340
  providerUrl,