Resolvendo seletores de linguagem natural
Um script .demo diz o que significa:
selectors:
composer = ask "a caixa de mensagem na parte inferior do chat"
Seu trabalho é encontrar o que realmente está no DOM ativo e escrever em
scripts/<nome>.selectors.json, para que script.mjs possa compilá-lo em um seletor comum
e driver.mjs possa gravar uma tomada determinística.
Você é o resolvedor. Nada no pipeline chama um modelo — script.mjs
lê o arquivo auxiliar e se recusa a compilar sem ele. A resolução acontece aqui,
uma vez, antes da gravação, porque o tempo da tomada é a linha do tempo do vídeo e uma
ida e volta do modelo durante uma execução cairia no vídeo final como tempo morto.
Antes de começar
- O aplicativo deve estar acessível e, se precisar de login, já autenticado no Chrome do usuário. Esse é o propósito de usar a extensão: ela acessa a sessão real, então não há ritual de SSO nem código de e-mail para preencher.
- Leia o script primeiro. Você precisa do cabeçalho
app:/target:para a URL e as batidas, porque os seletores devem ser resolvidos na ordem do fluxo (veja abaixo).
Procedimento
-
Obtenha a lista de trabalho.
cd pipeline node script.mjs ../scripts/<nome>.demo --intentsJSON na saída padrão: cada seletor
ask, sua intenção e sua resolução atual (nullse não resolvido). Resolva os nulos. Re-resolva o restante apenas se o usuário disser que o fluxo está quebrado. -
Carregue as ferramentas do navegador em UMA chamada ToolSearch, depois chame
tabs_context_mcpantes de qualquer outra coisa. Crie uma nova aba comtabs_create_mcpa menos que o usuário tenha apontado para uma já existente. -
Percorra o fluxo na ordem das batidas. Esta é a parte fácil de errar: muitos seletores não existem na página inicial. Uma tabela de resultados aparece apenas após o envio de uma pergunta; um controle "Parar" existe apenas enquanto algo está sendo gerado. Então, percorra as batidas do script, resolvendo cada
askno momento em que seu elemento está na tela.Dados reais. Antes de qualquer etapa que crie, modifique, envie ou exclua algo, pare e pergunte ao usuário.
feature.demoenvia quatro mensagens para uma conta real do Salesmessage. Passe o mouse em vez de clicar quando você só precisa localizar algo, e nunca clique em um controle destrutivo para "ver o que é". -
Encontre o elemento e escolha o identificador mais durável. Em ordem de preferência:
Prefira Por quê [data-testid=...],data-test,data-qacolocados lá para serem selecionados; sobrevivem a redesigns um idestávelbom, a menos que pareça gerado ( :r3a:,id-1a2b)botão/link pelo nome acessível exato → btn:Nomeo btn:do driver corresponde ao texto,aria-labeleimg[alt]interno[aria-label=...],[role=...]estáveis e geralmente semânticos estrutura semântica ( main table,nav a[href="/teams"])sobrevive a mudanças de estilo Nunca use: cadeias
nth-child, nomes de classe gerados (css-1a2b3c, longas pilhas Tailwind), ou qualquer coisa baseada em conteúdo gerado. Esse último é a armadilha que todo este recurso existe para evitar — a resposta de um modelo é redigida de forma diferente a cada execução, então um seletor que corresponde ao seu texto é um cara ou coroa. Ancore no contêiner ou em um sinal de conclusão. -
Verifique antes de escrever. Para cada candidato, confirme com
javascript_toolque corresponde a exatamente um elemento e que o elemento está visível:const q = 'div[data-testid="chat-input"]'; const n = document.querySelectorAll(q); console.log(JSON.stringify({ q, count: n.length, visible: n[0] ? !!(n[0].offsetWidth || n[0].offsetHeight) : false, tag: n[0]?.tagName, editable: n[0]?.isContentEditable, }));Leia de volta com
read_console_messages. Mais de uma correspondência, ou zero, significa continuar procurando — não escreva um seletor que você não viu corresponder. -
Escreva o arquivo auxiliar,
scripts/<nome>.selectors.json:{ "resolvedAgainst": "https://claude.ai/new", "resolvedAt": "2026-08-10T09:00:00Z", "resolvedBy": "claude-in-chrome", "selectors": { "a caixa de mensagem na parte inferior do chat": { "spec": "div[data-testid=\"chat-input\"]", "evidence": "contenteditable tiptap div, aria-label 'Write a message', 1 correspondência, visível" } } }Indexado pelo texto da intenção, literalmente do script — renomear um seletor ou reordenar o bloco não deve órfão de uma resolução.
specé o que quer que o driver aceite: um seletor puppeteer bruto,btn:Texto|Alt, ou::-p-text(substring).evidenceé o que você realmente observou; escreva para que a próxima pessoa possa dizer se ainda é válido. -
Confirme e devolva.
node script.mjs ../scripts/<nome>.demo --intents # espera 0 não resolvidos node script.mjs ../scripts/<nome>.demo --print # compilaRelate o que você resolveu, entre o que teve que adivinhar e qualquer coisa que você pulou porque precisava de uma etapa destrutiva.
Quando você não puder resolver um
Diga e pare; não invente um seletor. Uma intenção não resolvida é um erro de compilação limpo antes mesmo do Chrome ser iniciado. Um seletor errado falha no meio da tomada, depois que a gravação começou — o lugar caro para falhar. Se dois elementos corresponderem igualmente bem à descrição, pergunte ao usuário qual eles queriam em vez de escolher.
Armadilhas
| Sintoma | Causa / correção |
|---|---|
| O elemento não está na página | Aparece mais tarde no fluxo. Percorra as batidas em ordem; resolva cada intenção quando seu elemento estiver realmente na tela |
| Dois elementos correspondem | A descrição é ambígua. Especifique com o usuário e depois reformule o ask no script para que a próxima resolução seja inequívoca |
| Seletor funciona agora, quebra na próxima execução | Está baseado em conteúdo gerado ou em uma classe gerada. Reancore em um testid, um role ou um contêiner |
Um âncora wait ... gone | São transitórios por natureza (um botão Parar enquanto gera). Resolva enquanto está na tela e prefira aria-label — muda menos que o texto |
| Resolução desviou após um redesign | Apague apenas aquela chave do arquivo auxiliar e execute novamente. O .demo não muda; esse é o objetivo |
| Diálogo bloqueia a extensão | Não clique em coisas que abrem confirm/alert. Um diálogo modal congela a extensão até ser descartado manualmente |