ego-browser
O ego-browser expõe um navegador Chromium real através de um runtime Node.js acessível via CLI. Suas fachadas pré-carregadas page, page.locator(...), browser e taskSpaces seguem os nomes e formatos de chamada do Playwright; taskSpaces, site, fetch e cdp fornecem capacidades específicas do ego-browser.
Para problemas de configuração, instalação ou conexão, leia references/install.md.
Execute o trabalho do navegador com a ferramenta Bash como ego-browser nodejs <<'EOF' ... EOF. Coloque o JavaScript diretamente no heredoc; não crie um arquivo .js, importe Playwright, inicie outro navegador ou invente nomes de auxiliares.
Um heredoc é apenas o contêiner do JavaScript; a invocação Bash é a rodada de execução. Padrão: uma invocação Bash para toda a tarefa do navegador. Cada await é uma operação interna, não um limite de etapa. Antes de iniciar, codifique cada observação, ação, espera, extração, verificação e alternativa delimitada previsível no script. Use os resultados do navegador imediatamente em JavaScript e continue adaptando no processo até que a tarefa seja concluída; não saia meramente para inspecionar saída intermediária ou planejar a próxima ação. Inicie outro comando Bash apenas para controle obrigatório do usuário ou externo, inspeção visual que não pode acontecer no processo, ou uma falha em nível de processo que o script não pode recuperar.
Escolha a rota confiável com menos estado antes de inspecionar controles da página. Quando a tarefa especificar um resultado ou restrições, mas não uma interação obrigatória, prefira um estado já correto ou uma URL estável conhecida ou rota do site que os codifique diretamente; verifique o estado final da meta resultante em vez de reproduzir filtros, classificação ou navegação equivalentes através da UI. Use controles de página quando o usuário solicitou essa interação, a interação em si está sob teste, ou nenhum equivalente confiável é conhecido. Nunca invente uma rota frágil.
Trate uma pós-condição já satisfeita como trabalho concluído. Antes de manipular um controle cujo valor obrigatório já pode estar visível, execute apenas a menor leitura necessária para decidir esse estado. Se corresponder, não abra seu editor, reproduza a interação ou leia novamente; continue diretamente para os resultados insatisfeitos restantes. Palavras como "definir", "selecionar" ou "garantir" descrevem o estado final obrigatório, a menos que o usuário exija explicitamente a transição ou a interação em si esteja sob teste.
Separe o trabalho do navegador da conclusão do terminal. useOrCreate inicia ou retoma um objetivo do usuário; mantenha seu task.id retornado e reutilize o mesmo id ou exato mesmo nome até que esse objetivo seja terminal. Mantenha cada observação, ação, espera, extração e verificação previsível compacta, mas não chame taskSpaces.complete(...) em uma invocação Bash que ainda está determinando se a meta está satisfeita. Primeiro termine o trabalho do navegador e imprima evidências de que cada resultado solicitado e qualquer escopo obrigatório ou limite de cobertura foi comprovado. Somente após revisar essa saída anterior, uma invocação Bash final dedicada pode concluir o espaço de tarefa original; ela não realiza nenhum trabalho de page ou browser. Este commit de ciclo de vida único é a exceção ao padrão de uma invocação, não uma etapa ou rodada do navegador. Resultados parciais não vazios ou plausíveis, uma página travada, tentativas esgotadas ou uma tentativa de fallback não são evidências de conclusão. keep: true preserva um resultado terminal para o usuário; não mantém uma tarefa inacabada viva.
Congele a janela de tempo para trabalho com data atual ou relativa. Estabeleça "hoje/atual/mais recente" uma vez a partir do ambiente do usuário/tarefa ou do estado da página verificado explicitamente antes de coletar registros. Trate carimbos de data/hora de conteúdo como dados, não como o relógio. Registros mais antigos revelados por rolagem, virtualização, recarga, cache ou um lote de resultados alterado não devem substituir essa âncora. Continue avaliando os registros contra a janela original; não redefina silenciosamente a tarefa para a data de conteúdo mais recente observada.
Início rápido
Cada exemplo nesta habilidade é deliberadamente composto. Adapte sua URL, seletores e dados à tarefa do usuário.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('inspecionar página de exemplo')
await browser.openOrReuseTab('https://example.com', { wait: true, timeout: 20000 })
const heading = await page.getByRole('heading').first().innerText()
const info = await page.info()
if (!heading || !('url' in info)) throw new Error('A página de exemplo não estava pronta')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Mantenha todo o trabalho previsível dentro do script até que a tarefa seja concluída. Emita resultados finais com console.log(...).
Padrões compostos
Extrair, escolher, navegar, verificar
Em uma página de lista ou pesquisa, extraia candidatos estruturados antes de escolher. Mantenha a extração, escolha, ação, espera, verificação e limpeza em uma invocação Bash.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('comparar resultados de pesquisa')
await browser.openOrReuseTab('https://example.com/search?q=automacao+navegador', {
wait: true,
timeout: 20000,
})
const cards = page.locator('article')
const items = await cards.evaluateAll((nodes) =>
nodes.map((node) => ({
title: node.querySelector('h2')?.textContent?.trim(),
href: node.querySelector('a')?.href,
})),
)
const chosenIndex = items.findIndex((item) => item.title && item.href)
if (chosenIndex < 0) throw new Error('Nenhum resultado utilizável: ' + JSON.stringify(items))
const before = await page.url()
const navigation = page.waitForURL((url) => url.href !== before, { timeout: 15000 })
await cards.nth(chosenIndex).getByRole('link').first().click()
if (!(await navigation)) throw new Error('O resultado escolhido não navegou')
const info = await page.info()
if (!('url' in info) || info.url === before) throw new Error('Navegação não foi verificada')
const result = { chosen: items[chosenIndex], opened: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Preencher, acionar, esperar, ler de volta
Registre esperas de solicitação/resposta antes da ação que as aciona, em seguida, verifique o estado da página resultante em vez de tratar o clique como sucesso.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('pesquisar pedidos')
await browser.openOrReuseTab('https://example.com/orders', { wait: true, timeout: 20000 })
const responsePromise = page.waitForResponse(
(response) => response.url().includes('/api/orders') && response.ok(),
{ timeout: 15000 },
)
await page.getByLabel('Pesquisar pedidos').fill('pendente')
await page.getByRole('button', { name: /pesquisar/i }).click()
const response = await responsePromise
const rows = await page.locator('table tbody tr').allInnerTexts()
if (!rows.length) throw new Error('Pesquisa concluída, mas não retornou linhas visíveis')
const result = { status: response.status(), rows }
console.log(JSON.stringify(result, null, 2))
EOF
Atualizar identificadores de aba, alternar, inspecionar
Trate targetId como um identificador de curta duração. Descubra, valide e use na mesma invocação Bash.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('revisar relatório gerado')
const tabs = await browser.listTabs({ includeChrome: false })
const reportTab = tabs.find((tab) => tab.url.includes('/reports/'))
if (!reportTab?.targetId) throw new Error('Aba de relatório não encontrada: ' + JSON.stringify(tabs))
await browser.switchTab(reportTab.targetId)
const info = await page.info()
const heading = await page.getByRole('heading').first().innerText()
if (!('url' in info) || !info.url.includes('/reports/')) throw new Error('Aba errada selecionada')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Mapa de runtime
page: navegação e estado (goto,reload,url,title,info), localizadores semânticos, esperas,snapshot,screenshot,screencast,evaluate,keyboard,mouse, downloads e drenagem de eventos.page.locator(selector): encadeamento e filtragem;first/nth/last; clicar, passar o mouse,dragTo,scrollIntoViewIfNeeded, formulário, teclado, upload, leitura de estado, coleção, avaliação de elemento, screenshot e métodos de espera.browser:listTabs,currentTab,switchTab,openOrReuseTab,closeTab,ensureRealTab,iframeTarget.taskSpaces:list,switch,new,useOrCreate,claim,complete,handOff,takeOver,waitForAgentControl.fetch.serverrealiza requisições do lado do Node;fetch.browserrealiza requisições na origem da página atual. Usecdpapenas como uma saída de emergência.console.logé o canal de saída. Useconsole.log(help('page')),console.log(help('locator'))ou outra chamadahelp(name)quando uma assinatura exata não estiver clara.
Regras de execução
page.url()é assíncrona no ego-browser; sempre useawait page.url(). Um predicadopage.waitForURL(...)recebe um objetoURL, então inspecioneurl.href,url.pathnameouurl.searchParams. Ele espera porloadpor padrão; usewaitUntil: 'commit'apenas quando prosseguir intencionalmente antes do carregamento.page.waitForURL,page.waitForLoadState,page.waitForSelector,locator waitForepage.waitForFunctionretornam um valor falso em caso de tempo limite. Verifique o resultado ou verifique imediatamente o estado obrigatório antes de continuar.- Registre esperas de solicitação, resposta ou navegação antes da ação que as aciona. Prefira esperas baseadas em estado; use
page.waitForTimeout(...)apenas para ajuste visual breve e mantenha-o em ou abaixo de 2000 ms. - Prefira localizadores semânticos estáveis. Quando a estrutura da página for desconhecida, colete os controles ou candidatos relevantes uma vez com
evaluateAll,allInnerTextsou outra leitura delimitada, derive as próximas ações em JavaScript e continue no mesmo heredoc em vez de enumerar palpites de seletor entre comandos. - Ações de elemento único e leituras obrigatórias—incluindo CSS puro e localizadores
xpath=puros—são estritos e esperam automaticamente. Para zero correspondências, confirme o carregamento, a aba ativa e o estado de modal/overlay antes de corrigir o localizador. Para múltiplas correspondências, inspecionecount()/allInnerTexts(), restrinja semanticamente ou comfilter(...), e usefirst()/nth()somente após confirmar que as duplicatas são legítimas. Deixe uma ação bem-sucedida levar o script adiante; leia o estado quando ele determinar uma ramificação e uma vez para as pós-condições finais obrigatórias da tarefa, não após cada ação. Um estado obrigatório já satisfeito não precisa de reprodução. - Em caso de falha, use uma observação direcionada para mudar de estratégia materialmente. Não repita localizadores ou comandos quase idênticos; mude para um caminho semântico, DOM ou visual estável com base na evidência.
- Preserve transições explicitamente solicitadas visíveis ao usuário e limites de parada. Quando um clique obrigatório pode navegar na aba atual ou abrir outra, clique uma vez e resolva o resultado a partir de
await page.url()mais umbrowser.listTabs()atualizado no mesmo script; não substitua o clique por navegação direta meramente porque seu destino é conhecido. Não engula falhas de ações obrigatórias.
Espaços de tarefa
Um espaço de tarefa é um contexto de navegação isolado com suas próprias abas que herda o estado de login do usuário. Selecione-o uma vez perto do início do primeiro script Bash com taskSpaces.useOrCreate(nameOrId). Se uma dependência externa tornar um comando posterior inevitável, selecione o mesmo task.id numérico retornado ou o mesmo nome curto de objetivo antes de continuar; crie um novo espaço apenas para um objetivo de usuário separado. Preserve fatos já verificados entre comandos em vez de reiniciar a configuração.
useOrCreate reutiliza ou cria espaços de propriedade do agente. Se o espaço correspondente for de propriedade do usuário, ele seleciona o espaço sem reivindicá-lo, então o trabalho do navegador atinge a parada rígida de controle do usuário. Após confirmação explícita do usuário para trabalhar lá, use taskSpaces.list() → taskSpaces.claim(id) → browser.listTabs() → browser.switchTab(targetId).
Cada espaço tem ownership: 'agent' | 'agentDelegatedToUser' | 'user':
| Operação em um espaço de propriedade do usuário | Comportamento |
|---|---|
taskSpaces.switch | Lança exceção; ele alterna apenas espaços de propriedade do agente |
taskSpaces.claim | Transfere a propriedade para o agente e seleciona o espaço |
taskSpaces.handOff / complete(..., { keep: true }) | Pula com { done: false, skipped: 'user-owned' } |
taskSpaces.complete(..., { keep: false }) | Reivindica, depois fecha o espaço |
taskSpaces.takeOver / waitForAgentControl | Não realiza verificação de propriedade |
Verifique o resultado done de handOff e complete antes de reivindicar sucesso.
Trate a conclusão como um commit de terminal separado da execução do navegador. Termine a invocação Bash de trabalho sem conclusão após capturar e imprimir a URL final, valores e outras evidências. Revise essa saída: cada pós-condição solicitada e qualquer escopo obrigatório ou limite de cobertura deve ser comprovado, não meramente provável. Se estiverem comprovados, execute uma invocação Bash final dedicada que chame taskSpaces.complete(nameOrId, { keep }) no máximo uma vez para o id original ou nome exato, verifique done, e não realize trabalho de page ou browser. Se algo estiver insatisfeito ou não comprovado, continue nesse mesmo espaço de tarefa original; uma correção, repetição ou fase posterior não é um novo objetivo. keep é obrigatório. O padrão é false; use true apenas quando o usuário pediu para manter a página finalizada, deve agir manualmente nela, ou o resultado não pode ser entregue como URL, arquivo, artefato ou resumo. Feche abas de rascunho conforme avança e mantenha apenas as abas que o usuário precisa.
Nunca codifique, copie manualmente ou renomeie um targetId para id. Obtenha e use dentro da invocação Bash atual. Se outro comando for genuinamente necessário, atualize browser.listTabs() e valide os resultados de find(...) antes de alternar ou fechar. browser.iframeTarget(...) retorna uma string de id de destino ou null, não um objeto.
Transferência de controle
Um erro "usuário está controlando", "inativo" ou "não atribuído" é uma parada rígida para toda a tarefa. Não tente novamente, contorne ou chame taskSpaces.takeOver automaticamente. Pergunte ao usuário e aguarde.
Para login, captcha ou outra etapa manual,
…
[truncado para tradução]