@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 +65 -26
- package/config/ui-testing.json +10 -1
- package/docs/jev-browser-mcp.md +65 -26
- package/mcp_servers/jev-browser-npm/src/config.mjs +91 -55
- package/mcp_servers/jev-browser-npm/src/flow.mjs +536 -92
- package/mcp_servers/jev-browser-npm/src/jev-client.mjs +22 -9
- package/mcp_servers/jev-browser-npm/src/server.mjs +33 -8
- package/package.json +1 -1
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.
|
|
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.
|
|
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`.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
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`, `
|
|
433
|
-
`
|
|
434
|
-
`
|
|
435
|
-
`
|
|
436
|
-
`
|
|
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`.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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}`;
|
package/config/ui-testing.json
CHANGED
|
@@ -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": ["
|
|
76
|
+
"blocked_resource_types": ["media"]
|
|
68
77
|
},
|
|
69
78
|
"jev": {
|
|
70
79
|
"max_diagnostic_items": 50,
|
package/docs/jev-browser-mcp.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.
|
|
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.
|
|
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`.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
|
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`, `
|
|
433
|
-
`
|
|
434
|
-
`
|
|
435
|
-
`
|
|
436
|
-
`
|
|
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`.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
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
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (
|
|
243
|
-
throw new JevBrowserError("config/ui-testing.json
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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,
|