@diegosouzacdv/jev-browser-mcp 0.2.0 → 0.3.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.
@@ -1,291 +1,354 @@
1
- # Automação de tela com Jev e Playwright
2
-
3
- ## Instalar em outros harnesses com npm/npx
4
-
5
- O repositório também contém um pacote Node independente do harness. Ele fala
6
- MCP por `stdio`, executa o Playwright no mesmo processo e pode ser iniciado por
7
- qualquer harness que aceite `command` e `args` para um servidor MCP. O pacote
8
- usa o mesmo `config/ui-testing.json` deste repositório; não precisa instalar o
9
- Python do orquestrador. As opções específicas do pacote ficam isoladas no
10
- bloco `jev_browser_mcp` para preservar o contrato do servidor Python.
11
-
12
- Instale a versão publicada do npm diretamente no harness. Para fixar uma
13
- versão em produção, use o número explícito no argumento do pacote:
14
-
15
- ```json
16
- {
17
- "mcpServers": {
18
- "jev-browser": {
19
- "command": "npx",
20
- "args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.2.0"],
21
- "env": {
22
- "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
23
- "JEV_BROWSER_MODE": "harness"
24
- }
25
- }
26
- }
27
- }
28
- ```
29
-
30
- O formato de interpolação de variáveis varia por harness. Injete a chave por
31
- um secret manager ou pelo ambiente do processo; não grave a chave no arquivo
32
- de configuração. Para instalar no projeto Node do próprio harness:
33
-
34
- ```sh
35
- npm install @diegosouzacdv/jev-browser-mcp
36
- npx --yes @diegosouzacdv/jev-browser-mcp --install-browser
37
- ```
38
-
39
- No modo `harness`, a instalação baixa uma vez o Chrome/Edge que o Playwright
40
- controlará. No modo `computer`, o pacote abre o Chrome/Edge instalado e usa um
41
- perfil persistente exclusivo em `browser.computer_user_data_dir`; personalize
42
- as escolhas com `JEV_BROWSER_MODE`, `JEV_BROWSER_CHANNEL` e
43
- `JEV_BROWSER_PROFILE`. O browser permanece aquecido enquanto o processo MCP
44
- estiver ativo e fecha quando o harness encerra o processo. `JEV_PROVIDER_URL`
45
- e `JEV_MODEL` podem substituir os valores centrais; a credencial continua no
46
- nome de variável declarado em `jev.credential_env`.
47
-
48
- Uma aplicação Node também pode importar `createJevBrowserServer` por
49
- `@diegosouzacdv/jev-browser-mcp/server` e conectar o servidor ao transporte MCP
50
- que ela já utiliza.
51
-
52
- O pacote é montado pela raiz do repositório, mas o campo `files` do `package.json`
53
- inclui somente o código Node, a configuração compartilhada e esta documentação
54
- (além do README que o npm inclui automaticamente). Ele não publica o restante
55
- do orquestrador. A instalação por npm é a recomendada; use a referência GitHub
56
- somente quando precisar experimentar uma revisão ainda não publicada.
57
-
58
- ### Contrato do pacote
59
-
60
- O executável oferece `choose_next_action` e `run_browser_flow`, mantém uma
61
- sessão do browser por processo e reutiliza essa sessão entre chamadas. O plano
62
- passado ao Jev continua declarativo: clique, preenchimento, seleção nativa,
63
- hover, espera, teclas aprovadas, asserções por elemento, upload restrito a uma
64
- raiz local configurada e reações idempotentes a um comentário único. Também
65
- aceita um nome de iframe para as ações que ocorrem dentro dele. Não aceita
66
- JavaScript enviado pelo harness, seletores livres nem coordenadas. Ele usa a
67
- biblioteca Playwright diretamente, sem iniciar um segundo servidor MCP do
68
- Playwright. O transporte MCP usa `stdio`; toda saída de diagnóstico vai para
69
- `stderr` para não misturar com JSON-RPC.
70
-
71
- O modo `computer` grava cookies no perfil exclusivo configurado, e conteúdo da
72
- página pode conter instruções maliciosas. O Jev recebe a captura acessível com
73
- uma instrução para tratar esse conteúdo como dado não confiável; não inclua
74
- segredos no fluxo, no resultado esperado ou nas descrições dos planos.
75
-
76
- Para um fluxo conhecido, o LLM do harness procura o cenário e os critérios de
77
- aceitação no projeto e chama `jev_browser.run_browser_flow` com planos
78
- candidatos declarativos. O MCP abre uma única sessão do Playwright, navega para
79
- a página inicial, captura o snapshot acessível e pede ao Jev que escolha um
80
- plano. Em seguida, executa o plano inteiro na mesma sessão e confere o
81
- resultado esperado na tela.
82
-
83
- Cada plano pode usar:
84
-
85
- - `click`, `type`, `hover` e `select_option` com papel/nome acessível exatos;
86
- - `wait_for_text` e `wait_for_condition` (`network_idle`, limitado pelo timeout
87
- de ação configurado);
88
- - `press_key` com `PageDown`, `PageUp`, `Home`, `End`, `ArrowDown`, `ArrowUp`,
89
- `Enter`, `Escape` ou `Tab`;
90
- - `assert_text`, `assert_value`, `assert_visible` e `assert_hidden`;
91
- - `upload_file` para input rotulado, botão que abre o seletor de arquivo ou
92
- dropzone;
93
- - `like_comment` e `unlike_comment`, que localizam uma linha pelo autor e texto,
94
- não repetem uma reação já no estado pedido e distinguem `Curtir` de
95
- `Descurtir`.
96
-
97
- Para ações em iframe, acrescente `"frame": "payment-iframe"`; o valor precisa
98
- corresponder ao atributo `name` ou `title` do iframe. O snapshot inclui o
99
- conteúdo acessível dos iframes nomeados dentro do escopo, limitado pela
100
- configuração. Se um alvo estiver ausente, o erro inclui até três nomes acessíveis
101
- próximos quando o snapshot os encontrar. O plano não aceita JavaScript enviado
102
- pelo harness, coordenadas ou seletores livres.
103
-
104
- Os aliases `value` → `text` em `type`/`wait_for_text` e `value` → `expected` em
105
- asserções são aceitos. `comment` também é aceito em planos e passos, mas é
106
- ignorado e não é enviado ao Jev. Campos fora do contrato continuam sendo
107
- recusados.
108
-
109
- ### Upload de arquivos
110
-
111
- Defina `JEV_BROWSER_UPLOAD_ROOT` como uma pasta absoluta que contenha os arquivos
112
- de fixture. Cada caminho passado ao fluxo também precisa ser absoluto; o MCP
113
- resolve links simbólicos e recusa qualquer arquivo fora dessa raiz. Só aceita
114
- arquivos regulares, até 5 arquivos por passo, 10 MiB por arquivo e 25 MiB no
115
- total do passo, conforme `jev_browser_mcp.browser` em `config/ui-testing.json`.
116
- Os bytes são lidos e validados no processo local antes de serem entregues ao
117
- Playwright. O MCP não envia esses bytes ao Jev nem os inclui diretamente na
118
- resposta; texto que o próprio site exibir na interface ainda pode aparecer no
119
- snapshot devolvido ao harness.
120
-
121
- ```json
122
- {"action":"upload_file","target":"input","label":"Nota fiscal","file_path":"/fixtures/nota.pdf"}
123
- ```
124
-
125
- Para uma área de arrastar, use o papel e o nome acessível da dropzone. Para um
126
- botão que abre a janela nativa, use `target: "button"`; sem `target`, a presença
127
- de `label` seleciona o input e `role`/`name` seleciona esse botão.
128
-
129
- ```json
130
- {"action":"upload_file","target":"dropzone","role":"region","name":"Anexos","file_paths":["/fixtures/a.pdf","/fixtures/b.png"]}
131
- ```
132
-
133
- Se `JEV_BROWSER_UPLOAD_ROOT` não estiver definido, a ação recusa a execução. O
134
- limite evita que um plano transforme o MCP em leitor arbitrário de arquivos do
135
- computador.
136
-
137
- Exemplo de chamada:
138
-
139
- ```json
140
- {
141
- "flow": "Adicionar o produto ao carrinho e confirmar o resumo",
142
- "initial_url": "http://127.0.0.1:4173/products/coffee",
143
- "expected_outcome": "Coffee added to cart",
144
- "options": {
145
- "fast_path": true,
146
- "snapshot_scope": "main",
147
- "capture_network_errors": true,
148
- "screenshot_on_failure": true
149
- },
150
- "candidate_plans": {
151
- "add_and_confirm": {
152
- "description": "Adicionar o produto visível ao carrinho e abrir o resumo",
153
- "steps": [
154
- {"action": "click", "role": "button", "name": "Add to cart"},
155
- {"action": "wait_for_text", "text": "Coffee added to cart"},
156
- {"action": "click", "role": "link", "name": "View cart"},
157
- {"action": "assert_text", "role": "heading", "name": "Order summary", "expected": "Coffee"}
158
- ]
159
- }
160
- }
161
- }
162
- ```
163
-
164
- O plano é montado pelo LLM do harness, mas o Jev escolhe qual plano fornecido
165
- deve executar usando o fluxo e o snapshot inicial. O Jev não cria ações, nomes
166
- de controles ou valores de formulário. Valores de `type` são usados localmente
167
- pelo Playwright e são removidos do texto enviado ao provedor e da evidência de
168
- retorno. Use valores de teste; autenticação deve ficar no perfil de navegador
169
- configurado.
170
-
171
- O MCP também mantém `choose_next_action` para fluxos exploratórios em que o
172
- harness precisa inspecionar e decidir entre ações uma por vez. Esse caminho é
173
- mais lento porque exige uma nova decisão e uma nova chamada de ferramenta por
174
- ação; prefira `run_browser_flow` quando os passos esperados puderem ser
175
- descritos antes da execução.
176
-
177
- ## Configuração
178
-
179
- Edite `config/ui-testing.json`. URL, modelo do provedor, variável de credencial
180
- e limites compartilhados ficam nos blocos `browser` e `jev`. As opções próprias
181
- do pacote Node ficam no bloco opcional `jev_browser_mcp`, que não altera o
182
- contrato lido pelo servidor Python. Preserve a estrutura completa exigida pelos
183
- validadores.
184
-
185
- `browser.mode` aceita `harness` ou `computer`:
186
-
187
- - `harness` usa Chrome headless e perfil isolado, adequado a execuções do
188
- harness e CI; o estado de autenticação é descartado ao final da chamada.
189
- - `computer` abre o Chrome ou Edge instalado em modo visível e usa o diretório
190
- persistente `browser.computer_user_data_dir`, separado por navegador. Não
191
- reutiliza o perfil pessoal já aberto. Faça login uma vez nesse perfil; os
192
- cookies permanecem nele entre chamadas.
193
-
194
- `browser.max_flow_steps` limita a soma de passos declarados entre os planos e
195
- `browser.max_text_entry_chars` limita cada valor digitado. O resultado contém
196
- `status`, o plano escolhido, as ações executadas, a última captura acessível e
197
- se o critério esperado apareceu. Quando existem asserções explícitas, `status`
198
- também pode ser `passed` com todas elas satisfeitas; `assertions_passed` registra
199
- esse resultado e `expected_outcome_visible` continua descrevendo somente o
200
- texto global. `incomplete` significa que nenhum critério foi comprovado;
201
- confiança do Jev não substitui essa verificação.
202
-
203
- `jev_browser_mcp.browser.max_action_timeout_seconds` limita esperas por ações,
204
- seletores e condições. `JEV_BROWSER_UPLOAD_ROOT` libera uploads somente dentro
205
- de uma pasta absoluta escolhida pelo operador.
206
- `jev_browser_mcp.browser.max_upload_files`, `max_upload_path_chars`,
207
- `max_upload_file_bytes` e `max_upload_total_bytes` limitam quantidade e tamanho.
208
- Não configure a raiz como o disco inteiro ou a pasta home: use uma pasta de
209
- fixtures dedicada.
210
-
211
- O `run_browser_flow` aceita `options` com `fast_path`, `snapshot_scope`,
212
- `block_trackers`, `capture_console_errors`, `capture_network_errors`,
213
- `screenshot_on_failure` e `trace_on_failure`. Sem override, os padrões são lidos
214
- de `jev_browser_mcp` em `config/ui-testing.json`: um plano candidato pula a chamada Decisions;
215
- captura de console e screenshot de falha ficam ligadas; trace, rede e bloqueio
216
- de recursos ficam desligados. `snapshot_scope` aceita `body`, `main` ou `dialog`.
217
-
218
- `block_trackers: true` bloqueia os domínios e tipos de recurso listados na
219
- configuração (analytics, Hotjar, fontes externas e mídia). Isso pode alterar o
220
- layout ou o comportamento do site, então a opção é desligada por padrão.
221
-
222
- Com `capture_console_errors` e `capture_network_errors`, o retorno traz
223
- `console_errors` e `network_failures`, limitados em quantidade e tamanho.
224
- Respostas HTTP 4xx/5xx e requisições que falham são listadas; query strings,
225
- fragmentos, valores de formulário e nomes de arquivo são removidos ou
226
- sanitizados. Em falhas, `screenshot_on_failure` salva screenshot local e retorna
227
- `screenshot_path`. `trace_on_failure: true` também grava um `.zip` compatível
228
- com o Trace Viewer do Playwright e retorna `trace_path`. O diretório padrão é
229
- `~/.cache/orquestrador/jev-browser-artifacts`; `JEV_BROWSER_ARTIFACT_DIR` pode
230
- substituí-lo por uma pasta absoluta fora do pacote. Screenshots e traces podem
231
- conter dados visíveis da aplicação: mantenha o diretório local protegido e
232
- compartilhe os arquivos somente se o teste permitir.
233
-
234
- `harness_browser` e `computer_browser` aceitam `chrome` ou `msedge`.
235
- `computer_user_data_dir` deve ficar fora do repositório e conter `{browser}`;
236
- o diretório persistente armazena dados de login e é resolvido sob a pasta home
237
- do usuário quando começa com `~`. O Playwright MCP fica desabilitado até
238
- `ORQUESTRADOR_MCP_PLAYWRIGHT_ENABLED=1` ser configurado no ambiente do harness.
239
-
240
- Defina o valor secreto na variável indicada por `jev.credential_env`, usando o
241
- secret manager ou ambiente do processo que inicia o harness. Não grave a chave
242
- em `config/ui-testing.json`. `provider_url` é o endpoint HTTPS completo da API
243
- Decisions. O cliente não
244
- segue redirects e recusa URL com credencial, query string ou fragmento. O Jev
245
- fica indisponível quando a política de MCP está em modo offline.
246
-
247
- Na primeira execução, aqueça uma vez o cache local do pacote declarado em
248
- `browser.playwright_mcp_package` com `npx --yes <pacote> --help`. O MCP inicia
249
- depois com `--offline`, evitando uma consulta ao registry npm em cada fluxo.
250
- Quando a versão configurada mudar, aqueça o novo pacote uma vez.
251
-
252
- ## Desempenho e evidência
253
-
254
- O pacote Node usa a decisão remota do Jev para escolher entre múltiplos planos.
255
- Com exatamente um plano e `fast_path` ligado (padrão), executa esse plano sem
256
- chamar a API Decisions; `jev_decisions` fica em zero. Inclua o fluxo completo em
257
- um plano candidato para evitar chamadas separadas ao harness; seleção,
258
- navegação, ações e asserções ficam em uma chamada MCP. O servidor Python
259
- `mcp_servers/jev_browser_server.py` mantém seu fluxo próprio e usa uma decisão
260
- remota do Jev por chamada. A sessão Playwright fecha antes de o MCP Python
261
- retornar. O perfil do modo `computer` preserva o login para chamadas seguintes;
262
- nesse servidor o processo e a janela não são reutilizados. O pacote Node mantém
263
- o browser aquecido até o harness encerrar o processo. O snapshot enviado ao Jev
264
- e devolvido ao harness remove o rodapé e, se ainda exceder o limite, mantém o
265
- início e o fim da captura com um marcador de truncamento. `snapshot_scope` pode
266
- limitar a captura a `main` ou `dialog`; iframes nomeados contidos nesse escopo
267
- também podem ser incluídos.
268
-
269
- A chamada composta aquecida deve ficar dentro da meta de 20 segundos em páginas
270
- que respondem normalmente; a inicialização fria, páginas lentas, MFA e conteúdo
271
- sob demanda podem excedê-la. O MCP devolve `total_ms`, `browser_session_ms`,
272
- `navigation_ms`, `initial_snapshot_ms`, `jev_decision_ms` e `browser_plan_ms`
273
- para localizar o custo. O teto observado em um fluxo sintético local anterior
274
- foi 6,411 s; isso não mede o Instagram nem garante o mesmo tempo em outros sites.
275
-
276
- O snapshot inicial, o fluxo, o resultado esperado e as descrições dos planos
277
- são enviados ao endpoint Decisions quando há mais de uma opção. Com fast-path,
278
- um plano não gera chamada remota. Os passos, valores digitados, valores
279
- esperados pelas asserções, caminhos e conteúdo dos arquivos não são enviados.
280
- Não inclua segredos em `flow`, `expected_outcome` ou nas descrições. A resposta
281
- retorna o plano escolhido quando houver decisão, custo/confiança do provedor
282
- quando disponíveis e o snapshot final sanitizado. O fluxo passa quando o texto
283
- esperado aparece no snapshot ou quando todas as asserções declaradas passam.
284
-
285
- `network_idle` é uma espera limitada e opcional; páginas com polling ou conexões
286
- contínuas podem atingir o timeout. Prefira `wait_for_text` e asserções de
287
- elemento quando houver um sinal de interface específico.
288
-
289
- Consulte a [introdução do Jev](https://docs.typesafe.ai/introduction) e o
290
- [tutorial da API Decisions no OpenRouter](https://openrouter.ai/docs/guides/community/jev-tutorial)
291
- para os tipos de resposta e autenticação.
1
+ # Automação de tela com Jev e Playwright
2
+
3
+ ## Instalar em outros harnesses com npm/npx
4
+
5
+ O repositório também contém um pacote Node independente do harness. Ele fala
6
+ MCP por `stdio`, executa o Playwright no mesmo processo e pode ser iniciado por
7
+ qualquer harness que aceite `command` e `args` para um servidor MCP. O pacote
8
+ usa o mesmo `config/ui-testing.json` deste repositório; não precisa instalar o
9
+ Python do orquestrador. As opções específicas do pacote ficam isoladas no
10
+ bloco `jev_browser_mcp` para preservar o contrato do servidor Python.
11
+
12
+ Instale a versão publicada do npm diretamente no harness. Para fixar uma
13
+ versão em produção, use o número explícito no argumento do pacote:
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "jev-browser": {
19
+ "command": "npx",
20
+ "args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.3.1"],
21
+ "env": {
22
+ "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
23
+ "JEV_BROWSER_MODE": "harness"
24
+ }
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ O formato de interpolação de variáveis varia por harness. Injete a chave por
31
+ um secret manager ou pelo ambiente do processo; não grave a chave no arquivo
32
+ de configuração. Para instalar no projeto Node do próprio harness:
33
+
34
+ ```sh
35
+ npm install @diegosouzacdv/jev-browser-mcp
36
+ npx --yes @diegosouzacdv/jev-browser-mcp --install-browser
37
+ ```
38
+
39
+ No modo `harness`, a instalação baixa uma vez o Chrome/Edge que o Playwright
40
+ controlará. No modo `computer`, o pacote abre o Chrome/Edge instalado e usa um
41
+ perfil persistente exclusivo em `browser.computer_user_data_dir`; personalize
42
+ as escolhas com `JEV_BROWSER_MODE`, `JEV_BROWSER_CHANNEL` e
43
+ `JEV_BROWSER_PROFILE`. O browser permanece aquecido enquanto o processo MCP
44
+ estiver ativo e fecha quando o harness encerra o processo. `JEV_PROVIDER_URL`
45
+ e `JEV_MODEL` podem substituir os valores centrais; a credencial continua no
46
+ nome de variável declarado em `jev.credential_env`.
47
+
48
+ Uma aplicação Node também pode importar `createJevBrowserServer` por
49
+ `@diegosouzacdv/jev-browser-mcp/server` e conectar o servidor ao transporte MCP
50
+ que ela já utiliza.
51
+
52
+ O pacote é montado em uma pasta temporária isolada: o publicador usa o campo
53
+ `files` do `package.json` para copiar somente o código Node, a configuração
54
+ compartilhada e esta documentação, que também vira o `README.md` da raiz do
55
+ pacote. Assim, o npm não inclui o README geral do orquestrador na página do MCP.
56
+ Para gerar e conferir o pacote antes de publicar, execute
57
+ `npm run pack:jev-browser-mcp`; para publicar uma versão já autenticada no npm, execute
58
+ `npm run publish:jev-browser-mcp`. A instalação por npm é a recomendada; use a
59
+ referência GitHub somente quando precisar experimentar uma revisão ainda não
60
+ publicada.
61
+
62
+ ### Contrato do pacote
63
+
64
+ O executável oferece `choose_next_action` e `run_browser_flow`, mantém uma
65
+ sessão do browser por processo e reutiliza essa sessão entre chamadas. O plano
66
+ passado ao Jev continua declarativo: clique, preenchimento, seleção nativa,
67
+ hover, espera, teclas aprovadas, asserções por elemento, upload restrito a uma
68
+ raiz local configurada e reações idempotentes a um comentário único. Também
69
+ aceita um nome de iframe para as ações que ocorrem dentro dele. Não aceita
70
+ JavaScript enviado pelo harness, seletores livres nem coordenadas. Ele usa a
71
+ biblioteca Playwright diretamente, sem iniciar um segundo servidor MCP do
72
+ Playwright. O transporte MCP usa `stdio`; toda saída de diagnóstico vai para
73
+ `stderr` para não misturar com JSON-RPC.
74
+
75
+ O modo `computer` grava cookies no perfil exclusivo configurado, e conteúdo da
76
+ página pode conter instruções maliciosas. O Jev recebe a captura acessível com
77
+ uma instrução para tratar esse conteúdo como dado não confiável; não inclua
78
+ segredos no fluxo, no resultado esperado ou nas descrições dos planos.
79
+
80
+ Para um fluxo conhecido, o LLM do harness procura o cenário e os critérios de
81
+ aceitação no projeto e chama `jev_browser.run_browser_flow` com planos
82
+ candidatos declarativos. O MCP abre uma única sessão do Playwright, navega para
83
+ a página inicial, captura o snapshot acessível e pede ao Jev que escolha um
84
+ plano. Em seguida, executa o plano inteiro na mesma sessão e confere o
85
+ resultado esperado na tela.
86
+
87
+ Cada plano pode usar:
88
+
89
+ - `click`, `type`, `hover` e `select_option` com papel/nome acessível exatos;
90
+ - `wait_for_text` e `wait_for_condition` (`network_idle`, limitado pelo timeout
91
+ de ação configurado);
92
+ - `press_key` com `PageDown`, `PageUp`, `Home`, `End`, `ArrowDown`, `ArrowUp`,
93
+ `Enter`, `Escape` ou `Tab`;
94
+ - `assert_text`, `assert_value`, `assert_visible` e `assert_hidden`;
95
+ - `upload_file` para input rotulado, botão que abre o seletor de arquivo ou
96
+ dropzone;
97
+ - `audit_accessibility` com axe-core para WCAG 2.1 A/AA;
98
+ - `like_comment` e `unlike_comment`, que localizam uma linha pelo autor e texto,
99
+ não repetem uma reação já no estado pedido e distinguem `Curtir` de
100
+ `Descurtir`.
101
+
102
+ Quando vários controles têm o mesmo papel e nome, `within` limita a busca a um
103
+ container acessível único, como uma linha ou card. `index` escolhe uma ocorrência
104
+ zero-based dentro desse escopo; sem `index`, o MCP exige exatamente um alvo.
105
+
106
+ ```json
107
+ {
108
+ "action": "click",
109
+ "role": "button",
110
+ "name": "Add to cart",
111
+ "within": {"role": "group", "name": "Sauce Labs Backpack"}
112
+ }
113
+ ```
114
+
115
+ ```json
116
+ {"action":"click","role":"button","name":"Add to cart","index":0}
117
+ ```
118
+
119
+ ### Captura de downloads
120
+
121
+ Marque o clique que deve iniciar um download com `expect_download: true`. O
122
+ Playwright começa a aguardar o evento antes do clique; após a transferência
123
+ terminar, o MCP verifica o tamanho, sanitiza o nome sugerido e copia o arquivo
124
+ para `JEV_BROWSER_ARTIFACT_DIR` (ou para o diretório de artefatos configurado).
125
+ O resultado inclui a lista `downloaded_files`, com nome, caminho local e bytes;
126
+ cada passo também contém o arquivo capturado. Os limites contam arquivos e bytes
127
+ por fluxo. A checagem de tamanho acontece depois da transferência do navegador,
128
+ antes de copiar para a pasta de artefatos.
129
+
130
+ ```json
131
+ {"action":"click","role":"button","name":"Export report","expect_download":true}
132
+ ```
133
+
134
+ `jev_browser_mcp.browser.max_download_files`, `max_download_file_bytes`,
135
+ `max_download_total_bytes` e `max_download_timeout_seconds` definem os limites.
136
+ O diretório de artefatos deve ser local e protegido: arquivos baixados podem
137
+ conter dados da aplicação.
138
+
139
+ ### Auditoria automatizada de acessibilidade
140
+
141
+ Use `audit_accessibility` depois de colocar a página no estado que deseja
142
+ verificar:
143
+
144
+ ```json
145
+ {"action":"audit_accessibility","standard":"wcag2aa"}
146
+ ```
147
+
148
+ O MCP usa `@axe-core/playwright` com as tags WCAG 2.0 e 2.1 A/AA. O retorno
149
+ resume as violações por regra, impacto e quantidade de nós, sem copiar HTML ou
150
+ trechos da página. Qualquer violação reprova esse fluxo. A auditoria automática
151
+ encontra problemas comuns, mas não comprova conformidade WCAG completa; combine-a
152
+ com revisão manual e testes com usuários assistivos.
153
+
154
+ Para ações em iframe, acrescente `"frame": "payment-iframe"`; o valor precisa
155
+ corresponder ao atributo `name` ou `title` do iframe. O snapshot inclui o
156
+ conteúdo acessível dos iframes nomeados dentro do escopo, limitado pela
157
+ configuração. Se um alvo estiver ausente, o erro inclui até três nomes acessíveis
158
+ próximos quando o snapshot os encontrar. O plano não aceita JavaScript enviado
159
+ pelo harness, coordenadas ou seletores livres.
160
+
161
+ Os aliases `value` → `text` em `type`/`wait_for_text` e `value` → `expected` em
162
+ asserções são aceitos. `comment` também é aceito em planos e passos, mas é
163
+ ignorado e não é enviado ao Jev. Campos fora do contrato continuam sendo
164
+ recusados.
165
+
166
+ ### Upload de arquivos
167
+
168
+ Defina `JEV_BROWSER_UPLOAD_ROOT` como uma pasta absoluta que contenha os arquivos
169
+ de fixture. Cada caminho passado ao fluxo também precisa ser absoluto; o MCP
170
+ resolve links simbólicos e recusa qualquer arquivo fora dessa raiz. Só aceita
171
+ arquivos regulares, até 5 arquivos por passo, 10 MiB por arquivo e 25 MiB no
172
+ total do passo, conforme `jev_browser_mcp.browser` em `config/ui-testing.json`.
173
+ Os bytes são lidos e validados no processo local antes de serem entregues ao
174
+ Playwright. O MCP não envia esses bytes ao Jev nem os inclui diretamente na
175
+ resposta; texto que o próprio site exibir na interface ainda pode aparecer no
176
+ snapshot devolvido ao harness.
177
+
178
+ ```json
179
+ {"action":"upload_file","target":"input","label":"Nota fiscal","file_path":"/fixtures/nota.pdf"}
180
+ ```
181
+
182
+ Para uma área de arrastar, use o papel e o nome acessível da dropzone. Para um
183
+ botão que abre a janela nativa, use `target: "button"`; sem `target`, a presença
184
+ de `label` seleciona o input e `role`/`name` seleciona esse botão.
185
+
186
+ ```json
187
+ {"action":"upload_file","target":"dropzone","role":"region","name":"Anexos","file_paths":["/fixtures/a.pdf","/fixtures/b.png"]}
188
+ ```
189
+
190
+ Se `JEV_BROWSER_UPLOAD_ROOT` não estiver definido, a ação recusa a execução. O
191
+ limite evita que um plano transforme o MCP em leitor arbitrário de arquivos do
192
+ computador.
193
+
194
+ Exemplo de chamada:
195
+
196
+ ```json
197
+ {
198
+ "flow": "Adicionar o produto ao carrinho e confirmar o resumo",
199
+ "initial_url": "http://127.0.0.1:4173/products/coffee",
200
+ "expected_outcome": "Coffee added to cart",
201
+ "options": {
202
+ "fast_path": true,
203
+ "snapshot_scope": "main",
204
+ "capture_network_errors": true,
205
+ "screenshot_on_failure": true
206
+ },
207
+ "candidate_plans": {
208
+ "add_and_confirm": {
209
+ "description": "Adicionar o produto visível ao carrinho e abrir o resumo",
210
+ "steps": [
211
+ {"action": "click", "role": "button", "name": "Add to cart"},
212
+ {"action": "wait_for_text", "text": "Coffee added to cart"},
213
+ {"action": "click", "role": "link", "name": "View cart"},
214
+ {"action": "assert_text", "role": "heading", "name": "Order summary", "expected": "Coffee"}
215
+ ]
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ O plano é montado pelo LLM do harness, mas o Jev escolhe qual plano fornecido
222
+ deve executar usando o fluxo e o snapshot inicial. O Jev não cria ações, nomes
223
+ de controles ou valores de formulário. Valores de `type` são usados localmente
224
+ pelo Playwright e são removidos do texto enviado ao provedor e da evidência de
225
+ retorno. Use valores de teste; autenticação deve ficar no perfil de navegador
226
+ configurado.
227
+
228
+ O MCP também mantém `choose_next_action` para fluxos exploratórios em que o
229
+ harness precisa inspecionar e decidir entre ações uma por vez. Esse caminho é
230
+ mais lento porque exige uma nova decisão e uma nova chamada de ferramenta por
231
+ ação; prefira `run_browser_flow` quando os passos esperados puderem ser
232
+ descritos antes da execução.
233
+
234
+ ## Configuração
235
+
236
+ Edite `config/ui-testing.json`. URL, modelo do provedor, variável de credencial
237
+ e limites compartilhados ficam nos blocos `browser` e `jev`. As opções próprias
238
+ do pacote Node ficam no bloco opcional `jev_browser_mcp`, que não altera o
239
+ contrato lido pelo servidor Python. Preserve a estrutura completa exigida pelos
240
+ validadores.
241
+
242
+ `browser.mode` aceita `harness` ou `computer`:
243
+
244
+ - `harness` usa Chrome headless e perfil isolado, adequado a execuções do
245
+ harness e CI; o estado de autenticação é descartado ao final da chamada.
246
+ - `computer` abre o Chrome ou Edge instalado em modo visível e usa o diretório
247
+ persistente `browser.computer_user_data_dir`, separado por navegador. Não
248
+ reutiliza o perfil pessoal já aberto. Faça login uma vez nesse perfil; os
249
+ cookies permanecem nele entre chamadas.
250
+
251
+ `browser.max_flow_steps` limita a soma de passos declarados entre os planos e
252
+ `browser.max_text_entry_chars` limita cada valor digitado. O resultado contém
253
+ `status`, o plano escolhido, as ações executadas, a última captura acessível e
254
+ se o critério esperado apareceu. Quando existem asserções explícitas, `status`
255
+ também pode ser `passed` com todas elas satisfeitas; `assertions_passed` registra
256
+ esse resultado e `expected_outcome_visible` continua descrevendo somente o
257
+ texto global. `incomplete` significa que nenhum critério foi comprovado;
258
+ confiança do Jev não substitui essa verificação.
259
+
260
+ `jev_browser_mcp.browser.max_action_timeout_seconds` limita esperas por ações,
261
+ seletores e condições. `JEV_BROWSER_UPLOAD_ROOT` libera uploads somente dentro
262
+ de uma pasta absoluta escolhida pelo operador.
263
+ `jev_browser_mcp.browser.max_upload_files`, `max_upload_path_chars`,
264
+ `max_upload_file_bytes` e `max_upload_total_bytes` limitam quantidade e tamanho.
265
+ Não configure a raiz como o disco inteiro ou a pasta home: use uma pasta de
266
+ fixtures dedicada.
267
+
268
+ Os limites de download ficam no mesmo bloco: `max_download_files`,
269
+ `max_download_file_bytes`, `max_download_total_bytes` e
270
+ `max_download_timeout_seconds`. `jev_browser_mcp.jev.max_accessibility_violations`
271
+ limita quantas descrições de violações axe entram no resultado; a contagem total
272
+ continua informada mesmo quando a lista é truncada.
273
+
274
+ O `run_browser_flow` aceita `options` com `fast_path`, `snapshot_scope`,
275
+ `block_trackers`, `capture_console_errors`, `capture_network_errors`,
276
+ `screenshot_on_failure` e `trace_on_failure`. Sem override, os padrões são lidos
277
+ de `jev_browser_mcp` em `config/ui-testing.json`: um plano candidato pula a chamada Decisions;
278
+ captura de console e screenshot de falha ficam ligadas; trace, rede e bloqueio
279
+ de recursos ficam desligados. `snapshot_scope` aceita `body`, `main` ou `dialog`.
280
+
281
+ `block_trackers: true` bloqueia os domínios e tipos de recurso listados na
282
+ configuração (analytics, Hotjar, fontes externas e mídia). Isso pode alterar o
283
+ layout ou o comportamento do site, então a opção é desligada por padrão.
284
+
285
+ Com `capture_console_errors` e `capture_network_errors`, o retorno traz
286
+ `console_errors` e `network_failures`, limitados em quantidade e tamanho.
287
+ Respostas HTTP 4xx/5xx e requisições que falham são listadas; query strings,
288
+ fragmentos, valores de formulário e nomes de arquivo são removidos ou
289
+ sanitizados. Em falhas, `screenshot_on_failure` salva screenshot local e retorna
290
+ `screenshot_path`. `trace_on_failure: true` também grava um `.zip` compatível
291
+ com o Trace Viewer do Playwright e retorna `trace_path`. O diretório padrão é
292
+ `~/.cache/orquestrador/jev-browser-artifacts`; `JEV_BROWSER_ARTIFACT_DIR` pode
293
+ substituí-lo por uma pasta absoluta fora do pacote. Screenshots e traces podem
294
+ conter dados visíveis da aplicação: mantenha o diretório local protegido e
295
+ compartilhe os arquivos somente se o teste permitir.
296
+
297
+ `harness_browser` e `computer_browser` aceitam `chrome` ou `msedge`.
298
+ `computer_user_data_dir` deve ficar fora do repositório e conter `{browser}`;
299
+ o diretório persistente armazena dados de login e é resolvido sob a pasta home
300
+ do usuário quando começa com `~`. O Playwright MCP fica desabilitado até
301
+ `ORQUESTRADOR_MCP_PLAYWRIGHT_ENABLED=1` ser configurado no ambiente do harness.
302
+
303
+ Defina o valor secreto na variável indicada por `jev.credential_env`, usando o
304
+ secret manager ou ambiente do processo que inicia o harness. Não grave a chave
305
+ em `config/ui-testing.json`. `provider_url` é o endpoint HTTPS completo da API
306
+ Decisions. O cliente não
307
+ segue redirects e recusa URL com credencial, query string ou fragmento. O Jev
308
+ fica indisponível quando a política de MCP está em modo offline.
309
+
310
+ Na primeira execução, aqueça uma vez o cache local do pacote declarado em
311
+ `browser.playwright_mcp_package` com `npx --yes <pacote> --help`. O MCP inicia
312
+ depois com `--offline`, evitando uma consulta ao registry npm em cada fluxo.
313
+ Quando a versão configurada mudar, aqueça o novo pacote uma vez.
314
+
315
+ ## Desempenho e evidência
316
+
317
+ O pacote Node usa a decisão remota do Jev para escolher entre múltiplos planos.
318
+ Com exatamente um plano e `fast_path` ligado (padrão), executa esse plano sem
319
+ chamar a API Decisions; `jev_decisions` fica em zero. Inclua o fluxo completo em
320
+ um plano candidato para evitar chamadas separadas ao harness; seleção,
321
+ navegação, ações e asserções ficam em uma chamada MCP. O servidor Python
322
+ `mcp_servers/jev_browser_server.py` mantém seu fluxo próprio e usa uma decisão
323
+ remota do Jev por chamada. A sessão Playwright fecha antes de o MCP Python
324
+ retornar. O perfil do modo `computer` preserva o login para chamadas seguintes;
325
+ nesse servidor o processo e a janela não são reutilizados. O pacote Node mantém
326
+ o browser aquecido até o harness encerrar o processo. O snapshot enviado ao Jev
327
+ e devolvido ao harness remove o rodapé e, se ainda exceder o limite, mantém o
328
+ início e o fim da captura com um marcador de truncamento. `snapshot_scope` pode
329
+ limitar a captura a `main` ou `dialog`; iframes nomeados contidos nesse escopo
330
+ também podem ser incluídos.
331
+
332
+ A chamada composta aquecida deve ficar dentro da meta de 20 segundos em páginas
333
+ que respondem normalmente; a inicialização fria, páginas lentas, MFA e conteúdo
334
+ sob demanda podem excedê-la. O MCP devolve `total_ms`, `browser_session_ms`,
335
+ `navigation_ms`, `initial_snapshot_ms`, `jev_decision_ms` e `browser_plan_ms`
336
+ para localizar o custo. O teto observado em um fluxo sintético local anterior
337
+ foi 6,411 s; isso não mede o Instagram nem garante o mesmo tempo em outros sites.
338
+
339
+ O snapshot inicial, o fluxo, o resultado esperado e as descrições dos planos
340
+ são enviados ao endpoint Decisions quando há mais de uma opção. Com fast-path,
341
+ um plano não gera chamada remota. Os passos, valores digitados, valores
342
+ esperados pelas asserções, caminhos e conteúdo dos arquivos não são enviados.
343
+ Não inclua segredos em `flow`, `expected_outcome` ou nas descrições. A resposta
344
+ retorna o plano escolhido quando houver decisão, custo/confiança do provedor
345
+ quando disponíveis e o snapshot final sanitizado. O fluxo passa quando o texto
346
+ esperado aparece no snapshot ou quando todas as asserções declaradas passam.
347
+
348
+ `network_idle` é uma espera limitada e opcional; páginas com polling ou conexões
349
+ contínuas podem atingir o timeout. Prefira `wait_for_text` e asserções de
350
+ elemento quando houver um sinal de interface específico.
351
+
352
+ Consulte a [introdução do Jev](https://docs.typesafe.ai/introduction) e o
353
+ [tutorial da API Decisions no OpenRouter](https://openrouter.ai/docs/guides/community/jev-tutorial)
354
+ para os tipos de resposta e autenticação.