Algumas coisas que o loop precisa verificar não vivem na web nem num arquivo — elas vivem num app nativo do macOS: um toggle em Ajustes do Sistema, um valor numa célula do Numbers, uma janela que deveria ter se movido. O CLI computer-use (alias cu) controla esses apps a partir do shell através da Accessibility API — para que um agente possa operar um app que não tem MCP, ou provar que uma mudança de GUI realmente aterrissou na fronteira do desktop. Sua promessa definidora: ele é não-bloqueante — nunca toma o seu mouse, nunca digita no seu teclado, nunca traz o app para frente, e quando não consegue fazer algo, diz isso honestamente em vez de fingir sucesso.
A maior parte das ferramentas alcança o mundo através de texto — o shell, arquivos, a web. Mas muito software real é uma janela com botões: Ajustes do Sistema, Calculadora, Notas, Finder, Mail. Para checar que uma mudança que você fez realmente aparece ali, ou para operar um app que não expõe API alguma, você precisa de uma mão que consiga alcançar a GUI.
computer-use é essa mão — mas uma mão cuidadosa. Ela fala com os apps através da Accessibility API embutida do macOS: exatamente o mesmo canal que um leitor de tela usa para saber "há um botão aqui rotulado Save". Por esse canal ela consegue ler o que está na tela (cada elemento, seu papel, seu rótulo, sua posição), pressionar um botão e definir o texto de um campo.
O que ela deliberadamente não faz é mover o seu mouse físico ou teclar no seu teclado. Uma ferramenta de automação comum forja a entrada: ela teleporta o cursor para um ponto e sintetiza um clique, então o seu ponteiro salta e o app em que você estava digitando perde o foco. computer-use recusa isso. Ela pede ao app diretamente — "pressione o seu botão Save" — e o app o faz, enquanto o seu cursor permanece exatamente onde você o deixou. Você pode continuar trabalhando em outra janela o tempo todo.
A pegadinha é a parte honesta. O canal de acessibilidade não consegue expressar tudo o que um mouse real consegue — não há como, por exemplo, arrastar para selecionar uma região de canvas vazio. Quando você pede algo que o AX não consegue fazer, o CLI retorna um erro simples em vez de fingir que funcionou. Para o loop, essa honestidade é o valor inteiro: uma ferramenta que nunca mente sobre o que aconteceu é uma ferramenta sobre a qual você pode construir um Proof Gate.
Pense nisso como… um prédio com um interfone em cada mesa. Um entregador insistente entraria, pegaria a sua mão e faria você assinar o recebimento de uma encomenda — isso é entrada sintética. computer-use, em vez disso, aperta o interfone e pede à pessoa na mesa que assine. Você não sente nada, a sua própria caneta fica na sua mão — e se não houver interfone em alguma mesa, o entregador diz "não consigo alcançar essa" em vez de forjar uma assinatura. Onde a analogia quebra: não há humano no loop aqui; a "pessoa na mesa" é o próprio app respondendo a uma requisição de acessibilidade.
O pacote é @appfyai/computer-use-cli (repo github.com/appfyai/computer-use-mcp). Ele dirige apps através da Accessibility API do macOS via uma pequena ponte nativa em Swift. É um CLI de execução única que qualquer agente pode chamar a partir do shell: computer-use <cmd> ou o alias cu <cmd>. A partir da 1.0.7 a ponte nativa vem embutida no pacote, então uma instalação global roda de qualquer diretório sem checkout.
Três camadas, por alvo. Uma página web → as ferramentas de browser (claude-in-chrome / agent-browser), que conhecem o DOM. Um app com MCP ou API próprio dedicado (Slack, Linear, Calendar) → use esse. Um app nativo do macOS sem MCP, ou uma mudança de GUI que você precisa verificar dirigindo o app de fato → essa é exatamente a faixa deste CLI. Ele é só para macOS e exige a permissão de Acessibilidade do sistema mais uma permissão por app.
Não há absolutamente nenhuma injeção sintética de mouse/teclado via CGEvent em lugar algum da ferramenta. Isso é deliberado e permanente: o operador pode continuar usando a máquina enquanto um agente dirige outros apps em segundo plano. O custo — o AX só consegue fazer o que ele expõe — é pago de volta em confiança: um resultado diferente de ok significa genuinamente que a ação não aconteceu, que é precisamente o que um gate de verificação precisa.
Aqui está a ideia única na qual o resto da lição se apoia. Tanto uma ferramenta de automação comum quanto o computer-use podem acabar pressionando o mesmo botão Save. A diferença é o caminho que cada um toma para chegar lá — e o que esse caminho custa a você.
(1) Nenhum movimento de cursor. Um AX press mira um elemento, não um ponto da tela — o seu ponteiro físico nunca se move. (2) Nenhuma tecla. type define o AXValue de um elemento; não sintetiza eventos de tecla, então nunca cai em qualquer campo que você por acaso tenha focado. (3) Nenhuma elevação de janela. O app alvo não é ativado nem trazido para frente; ele pode agir enquanto minimizado ou atrás de outras janelas. Juntas, é por isso que o operador pode continuar trabalhando sem interrupção.
Como não há fallback sintético, qualquer coisa que o AX não consiga expressar simplesmente falha. Um arrasto pelo espaço vazio, uma combinação de teclas, enviar Enter — nenhum tem expressão AX, então o CLI retorna uma string de erro explícita. Não há tentativa silenciosa de melhor esforço. Essa é a propriedade da qual o Proof Gate depende.
Quando você pede ao computer-use para fazer algo, ele roda uma curta série de perguntas sim/não antes de tocar o app. Se cada gate for um "sim", ele executa a ação através da Acessibilidade e reporta ok. O primeiro "não" o detém — e, em vez de forjar um resultado, ele retorna um erro honesto.
Escolha uma ação abaixo, depois pressione Próximo para acompanhá-la pelos gates. Veja uma requisição scroll limpa recusar no último gate em vez de mover o seu cursor real.
ok.Comece aqui
Um comando chega ao CLI
Pressione Próximo para acompanhar clicar um botão por cada gate. Troque a ação acima para ver quais passam e qual recusa.
Autorizado? — até você rodar grant --app <Name>, os comandos retornam {"permission_required": true}. Elemento na árvore? — click/type resolvem um alvo a partir da árvore de acessibilidade (por --element-index, ou por --x/--y mapeado ao elemento AX naquele ponto); um alvo não-gravável ou ausente falha. O AX consegue expressar? — é aqui que mora o erro honesto. click = ax_press; type = ax_value; drag na barra de título da janela = ax_window_position; drag de slider = ax_slider_value. Um scroll livre ou um arrasto de seleção não tem expressão AX, então ele retorna Could not perform drag via accessibility API em vez de mover o seu cursor real para forjar.
O fluxograma disse que o AX passa ao redor do seu cursor. Isto deixa você sentir isso. Abaixo está um substituto minúsculo da sua tela: o seu próprio ponteiro (a seta) descansa numa nota de texto enquanto um agente pressiona Save em outra janela.
Experimente as duas ferramentas. Com entrada sintética, cada ação arranca o ponteiro pela tela e a sua nota perde o foco. Com computer-use (AX), as mesmas ações acontecem — e o ponteiro nunca se mexe. Observe a leitura e a seta.
A seta preta é o seu ponteiro físico. Sob AX ele fica parado; sob entrada sintética ele salta para o botão.
Ferramenta ativa
computer-use (AX)
As ações passam pela Accessibility API. O seu ponteiro e o seu teclado são seus; o app responde sem ser trazido para frente.
O que esta ferramenta toca
O que aconteceu com a sua mesa
Um clique sintético é um CGEvent real entregue à janela sob o cursor — o que significa que o SO primeiro move o cursor para lá e troca a janela-chave, roubando o foco de onde quer que você estivesse digitando. Um AX press é uma mensagem a um AXUIElement específico ("execute a sua ação padrão"); o ponteiro e o foco de teclado do sistema de janelas nunca são consultados, então o seu cursor continua piscando na sua nota. O mesmo vale para type: definir o AXValue escreve direto no elemento alvo sem uma troca de foco ou um único evento de tecla.
Você abre mão da capacidade de fazer coisas que só um ponteiro real consegue (arrastar para selecionar canvas vazio, passar o mouse sobre tooltips, atalhos no nível do SO). Em troca, a ferramenta é segura para rodar sem supervisão numa máquina que outra pessoa está usando — o requisito definidor de um loop AFK que verifica GUIs em segundo plano.
Alcançar apps é poderoso, então a autorização é deliberadamente estreita. Há duas fechaduras, não uma. Primeiro a permissão de Acessibilidade do macOS, válida para todo o sistema (concedida uma vez, em Ajustes do Sistema). Depois — separadamente — uma permissão por app: você autoriza explicitamente cada app que quer dirigir. Até um app ser autorizado, todo comando contra ele retorna permission_required e não faz nada.
Então um agente que recebeu permissão para dirigir a Calculadora não pode de repente começar a cutucar o Mail. Cada app é a sua própria porta com a sua própria chave.
Pense nisso como… um cartão de acesso de um prédio. Ser contratado (a permissão do sistema) deixa você entrar no saguão. Mas o seu cartão é então programado para salas específicas — o laboratório do segundo andar, não a gaiola dos servidores. Uma sala nova significa uma autorização nova, concedida de propósito. Onde quebra: não há uma central de segurança observando; a lista de permissões é só configuração local que você controla.
Os comandos de permissão
computer-use grant --app <Name|bundleId> autoriza um app (retorna {"status":"granted"}). computer-use permissions lista o que está autorizado. computer-use revoke --app <Name> revoga. Uma resposta permission_required é a deixa para autorizar e tentar de novo.
A permissão de Acessibilidade do sistema (Ajustes do Sistema → Privacidade e Segurança → Acessibilidade) é o que o próprio macOS impõe a qualquer cliente AX. A permissão por app do CLI é uma segunda camada, mais fina, que a ferramenta mantém por cima: ela delimita em quais apps este CLI vai agir, de modo que uma permissão ampla de sistema não se traduza em "dirigir tudo". As duas são separadas — ter a permissão do sistema é necessário mas não suficiente; você ainda autoriza cada app.
Use o nome EXATO do app que o macOS reporta — ele pode estar localizado (Calculadora vs Calculator). computer-use list-apps mostra os apps em execução controláveis sob os nomes que você precisa passar. Você também pode autorizar pelo bundle identifier quando o nome de exibição for ambíguo.
Como cada app é opt-in, a lista de permissões é uma fronteira de segurança natural: uma execução AFK pode receber permissão para verificar um app de sandbox enquanto todo app sensível permanece não-autorizado e, portanto, intocável. Ações destrutivas dentro do app (apagar, enviar, comprar, sair) permanecem sob controle do usuário independentemente da permissão.
Antes de o computer-use conseguir pressionar um botão, ele precisa encontrá-lo. Faz isso lendo a árvore de acessibilidade do app — uma lista estruturada de cada elemento na tela, cada um com um papel (AXButton, AXTextField), um rótulo, uma posição e um índice numérico. Você pede esse instantâneo com state, localiza o seu alvo e usa o índice dele (ou o seu x/y) para agir.
Aqui está esse pipeline inteiro — de "quais apps eu posso sequer tocar" até "o clique aterrissou" — como cinco passagens de bastão. A lista fixa à esquerda leva você a qualquer etapa.
list-apps → grant → state → find index → click, depois uma segunda leitura de state confirma que aterrissou.Primeiro, veja o que é controlável. computer-use list-apps retorna os apps em execução que você pode dirigir, sob os nomes exatos que o macOS reporta. Você passará um desses nomes para todo comando posterior — copie-o ao pé da letra, porque ele pode estar localizado.
Autorize aquele único app: computer-use grant --app Calculator. Agora — e só agora — os comandos contra ele vão rodar. Pule isso e o state volta como permission_required e nada acontece.
computer-use state --app Calculator retorna um instantâneo JSON: uma tree aninhada de nós, cada um com um role, rótulo, frame (o seu retângulo na tela) e um index. Este é o seu mapa da janela — o mesmo mapa que um leitor de tela enxerga.
Varra a árvore atrás do nó que você quer — digamos o AXButton rotulado "=" — e anote o seu index. Esse número é a alça sobre a qual você vai agir. Lembre-se: os índices derivam conforme a UI muda, então leia o state de novo logo antes de clicar.
computer-use click --app Calculator --element-index <n> executa um AX press. Depois leia o state uma segunda vez e cheque que o elemento de display mudou. Essa segunda leitura é a prova — você nunca presume; você confirma.
# a árvore é uma string JSON de nós AX aninhados { "tree": { "role": "AXWindow", "label": "Calculator", "children": [ { "role": "AXStaticText", "index": 3, "value": "0" }, # o display { "role": "AXButton", "index": 17, "label": "7" }, { "role": "AXButton", "index": 24, "label": "=" }, # ← o alvo ... ] }, "screenshot": "<base64 …>" }
Rode computer-use state --app Calculator e passe-o por uma ferramenta JSON para varrer os papéis: computer-use state --app Calculator | jq '.tree'. Cada nó carrega role, index, frame e um rótulo/valor; você age sobre o index (ou --x/--y dentro do seu frame).
A superfície completa de comandos está documentada na skill: abra-a com grep -n "Commands" ~/.claude/skills/computer-use-cli/SKILL.md.
É aqui que o computer-use conquista o seu lugar no loop. A regra do loop é: nunca alegue que algo funciona — prove na fronteira real. Para uma mudança de código, a fronteira é uma execução de testes. Para uma mudança de GUI, a fronteira é o próprio app. O computer-use permite que o passo VERIFY dirija o app de fato e leia o resultado de volta, em vez de confiar num screenshot ou num "deve estar tudo bem".
O padrão é um loop minúsculo próprio: ler a árvore para capturar o estado-antes, agir via AX, ler de novo e comparar. Se a segunda leitura bater com o que você pretendia, o gate passa. Se a ação retornou um erro, o gate falha — honestamente.
Um screenshot prova que os pixels renderizaram, não que o estado está correto — e um modelo lendo o próprio screenshot pode alucinar o valor que esperava ver. Ler a árvore de acessibilidade retorna o AXValue real do elemento como uma string sobre a qual você pode fazer asserções programaticamente. Isso é uma checagem na fronteira real: a alegação "o toggle está ligado" é sustentada pelos próprios dados de acessibilidade do SO, não por uma descrição confiante.
Se o passo de agir retornar um resultado diferente de ok, o gate falha imediatamente — sem precisar "comparar". Como a ferramenta nunca forja sucesso, uma ação falha jamais pode se passar por um gate aprovado. Essa é exatamente a propriedade que um verificador precisa: o silêncio nunca é confundido com sucesso.
Este é o passo VERIFY tornado concreto. Abaixo está uma leitura ao vivo do estado de acessibilidade de um app — quatro elementos-chave e uma tabela por elemento. Ela começa no estado antes. Pressione Rodar AX click para executar um ax_press no botão "=", depois veja a leitura reler a árvore: os valores que mudaram acendem, e cada linha vira match assim que a sua releitura confirma o valor pretendido.
Essa segunda leitura — não o clique — é a prova. Aperte Reiniciar para voltar ao estado-antes e rodar de novo.
Estado de acessibilidade — Calculator
app autorizado · lido via computer-use state --app Calculator
| Elemento | AXValue / rótulo | Releitura |
|---|
Um único objeto guarda os valores dos elementos do app. A primeira renderização é o instantâneo antes. Pressionar o botão muta o modelo da forma como um ax_press no "=" faria (o display recalcula, o elemento "último pressionado" atualiza), depois a leitura simula uma segunda leitura de state e a compara com o resultado pretendido. As linhas viram match só quando o valor relido é igual ao que a ação deveria produzir — esse é o gate, não o clique.
Numa execução real: state (capturar antes) → click --element-index <n> (a pressionada, retorna {"method":"ax_press","result":"ok"}) → state (capturar depois) → fazer asserção sobre o value do elemento de display. O bloco "último resultado" é o próprio ok/erro do CLI; a tabela é a sua própria comparação. Ambos precisam concordar para o gate passar.
Aqui está uma execução em que um agente pediu ao computer-use para fazer quatro coisas — e uma delas era um scroll por um canvas vazio, que a Acessibilidade não consegue expressar. Uma ferramenta inferior teria movido o seu cursor real para forjar um scroll. O computer-use recusou e retornou um erro. Leia isto como um relatório de incidente: uma linha do tempo do que ele fez, o raio de impacto (zero — a sua mesa nunca foi tocada) e os achados a reconhecer.
O ponto não é que algo "falhou". O ponto é que a falha foi honesta: a ferramenta disse a verdade para que o loop pudesse contorná-la (usar as ferramentas de browser para aquele scroll, ou escolher uma ação expressável em AX) em vez de construir sobre uma mentira.
state --app Preview
Leu a árvore de acessibilidade. Encontrou a barra de ferramentas, a visualização da página e um slider de zoom. Nenhum cursor moveu; o Preview não foi trazido para frente.
ax · okclick --element-index 8 (botão Zoom In)
ax_press no botão da barra de ferramentas. Retornou {"result":"ok"}. Um state de acompanhamento confirmou que o valor do zoom subiu.
drag --element-index 12 (slider de zoom) → 150%
O arrasto de slider resolve para ax_slider_value — uma ação AX expressável. Definiu o valor; a releitura confirmou 150%.
rolar o canvas da página 400px para baixo
Um scroll livre sobre canvas vazio não tem expressão de Acessibilidade. Para fazê-lo, a ferramenta teria de mover o seu cursor real para cima do canvas e sintetizar um evento de roda — exatamente o que ela nunca deve fazer. Ela recusou.
erro honestoretornou: "Could not perform scroll via accessibility API"
Um resultado simples, diferente de ok. O agente agora sabe que deve direcionar este passo para outro lugar — não presumir que aconteceu.
0
Movimentos de cursor0
Teclas enviadas0
Apps trazidos à frente1
Erro honestoDois erros honestos documentados: um drag livre / de seleção retorna Could not perform drag via accessibility API (o AX não consegue expressar um arrasto de região), e um type contra um alvo não-gravável — por exemplo o display da Calculadora, que é AXStaticText, não um campo de texto — falha em vez de fingir que o definiu. O type também substitui o valor inteiro de um campo (é ax_value, não entrada por teclas): ele não consegue acrescentar, e não há nenhum comando de combinação de teclas / Enter / Tab.
A honestidade tem uma aresta áspera que vale conhecer: alguns sliders podem reportar ok para um arrasto sem que o estado subjacente realmente mude (observado num slider de volume dos Ajustes do Sistema, não-determinístico). A ferramenta de fato checa que o valor AX mudou, mas um slider cujo AXValue não está vinculado a um estado observável pode ler como "mudado" mesmo assim. A lição: quando um resultado importa, confirme-o contra um sinal independente — que é exatamente o gate de releitura da seção 8.
Juntando tudo: aqui está um passo VERIFY completo que usa o computer-use para provar que um cálculo da Calculadora aterrissa — autorizar, ler, agir, reler, fazer asserção. Leia de cima a baixo e você reconhecerá cada comando da lição. Note a última linha: ela não confia no ok; ela relê o state e faz asserção sobre o display.
# PROVAR: pressionar "=" após "7 × 6" mostra 42 no display da Calculadora. # Não-bloqueante: o operador pode continuar trabalhando o tempo todo. computer-use grant --app Calculator # 1 · autorização por app before=$(computer-use state --app Calculator) # 2 · ler a árvore (antes) # 3 · dirija-o — cada click é um ax_press, nenhum cursor se move computer-use click --app Calculator --element-index 17 # 7 computer-use click --app Calculator --element-index 22 # × computer-use click --app Calculator --element-index 14 # 6 res=$(computer-use click --app Calculator --element-index 24) # = → {"method":"ax_press","result":"ok"} after=$(computer-use state --app Calculator) # 4 · ler a árvore (depois) # 5 · o GATE: não confie no "ok" — faça asserção sobre o AXValue real display=$(echo "$after" | jq -r '.tree.children[] | select(.role=="AXStaticText") | .value') if [ "$display" = "42" ]; then echo "PASS · display reads 42 at the real boundary" else echo "FAIL · display reads '$display' (expected 42)" # honesto, não presumido fi
Os índices de elementos são ilustrativos — leia os seus com computer-use state --app Calculator | jq '.tree' e substitua, porque os índices derivam entre estados da UI. Releia o state logo antes de cada click se o layout puder mudar.
Todo comando, flag e método AX vive no arquivo da skill. Abra-o com grep -n "Commands\|Known issues\|Rules" ~/.claude/skills/computer-use-cli/SKILL.md. A invocação por local-checkout, o caminho da ponte embutida e a instalação pelo registry estão todos documentados ali.
Três perguntas rápidas. Escolha uma resposta em cada — ela corrige ao clicar e diz por quê.
O que torna o computer-use "não-bloqueante"?
Você pede a ele para rolar um canvas vazio. O que acontece?
Como você prova que um AX click realmente aterrissou?
state → click → state num app que você de fato usa, ou para conectar este CLI a um passo de Proof-Gate no seu próprio loop. A seguir: visual-teach — o motor que construiu este mesmo curso.