Pular para o conteúdo principal

Controle de tela

Uma máquina com tela pode ser operada como uma pessoa faria: tirar um print, clicar, digitar, apertar atalhos e rolar. Funciona pela CLI, pela API HTTP, pelos SDKs Go e Python e por um servidor MCP. Com o MCP, um agente como o Claude Code opera a VM do mesmo jeito que opera um navegador.

Tudo passa pelo visualizador da tela, o mesmo da página no navegador: a imagem sai dele, e mouse e teclado chegam aos dispositivos virtio-input da VM. Nada é instalado no guest.

A seta do agente​

Quem assiste pelo navegador vê onde a automação age: uma seta clara com contorno e brilho laranja desliza até o ponto de cada ação (1,1 s) e só então clica, como uma pessoa faria; um círculo pulsa no clique e um selo "digitando…" aparece nas teclas. Fica na tela por 30 s depois da última ação. Só as ações do controle de tela (CLI, API, SDKs e MCP) desenham a seta; o mouse de quem está na página continua sendo só o cursor dela.

O nome na barra e na aba da página é runner.codes; display_title no runner.code (ou --display-title) troca.

Pela CLI​

rc machine screen --name desk screenshot -o tela.jpg     # screen 1512x794 -> tela.jpg
rc machine screen --name desk click 640 400 # --button right, --double
rc machine screen --name desk type "ls -la"
rc machine screen --name desk key Return # ctrl+c, ctrl+alt+t, alt+F4, Up...
rc machine screen --name desk scroll 640 400 --up --amount 5
rc machine screen --name desk drag 100 100 400 300
rc machine screen --name desk move 640 400
echo '[{"action":"click","x":640,"y":400},{"action":"type","text":"ls\n"}]' | rc machine screen --name desk do

As coordenadas são pixels da tela atual, com a origem no canto superior esquerdo. A tela pode acompanhar a janela do navegador e mudar de tamanho; por isso, cada comando lê o tamanho na hora.

Ações​

AçãoCamposFaz
clickx, y, button (left, right, middle), doublemove até o ponto e clica
movex, ysó move o ponteiro (hover)
dragx, y, to_x, to_yaperta, arrasta por pontos intermediários e solta
typetextdigita, com Shift quando precisa; \n é Enter
keykeysuma tecla ou atalho: Return, Escape, Tab, ctrl+c, ctrl+shift+t, alt+F4, F5, Page_Down
scrollx, y, direction (up, down), amount (padrão 3)gira a roda no ponto
waitms (até 60000)espera entre ações

Uma lista com qualquer ação inválida é recusada inteira, antes de qualquer evento sair: um type pela metade ou um atalho sem a última tecla deixaria a máquina num estado estranho.

type digita como um teclado US e cobre só ASCII. Acento e emoji são recusados com uma mensagem. Para outro texto, use exec com xdotool type dentro da máquina (o desktop de exemplo já traz).

Pela API​

curl -s -o tela.jpg -D - localhost:8080/api/v1/machines/desk/screen | grep -i x-screen
# X-Screen-Width: 1512
# X-Screen-Height: 794
curl -s -X POST localhost:8080/api/v1/machines/desk/screen/actions \
-d '{"actions":[{"action":"click","x":640,"y":400},{"action":"type","text":"ls"},{"action":"key","keys":"Return"}]}'
# 204
RotaResposta
GET /api/v1/machines/:name/screen200 com a tela em JPEG e o tamanho em X-Screen-Width e X-Screen-Height
POST /api/v1/machines/:name/screen/actions204 quando todas as ações saíram; até 500 ações por chamada

Erros: 400 CONFIG_ERROR para ação inválida ou máquina sem tela; 409 INVALID_STATE para máquina desligada ou pausada; 502 SCREEN_UNAVAILABLE quando o visualizador da máquina não responde.

Pelos SDKs​

Go
shot, _ := c.Screenshot(ctx, "desk")                  // shot.JPEG, shot.Width, shot.Height
c.Click(ctx, "desk", 640, 400)
c.Type(ctx, "desk", "ls -la")
c.Key(ctx, "desk", "Return")
c.ScreenActions(ctx, "desk",
client.ScreenAction{Action: "scroll", X: 640, Y: 400, Direction: "up"},
client.ScreenAction{Action: "wait", Ms: 300},
client.ScreenAction{Action: "key", Keys: "ctrl+c"})
Python
shot = c.screenshot("desk")                           # shot.jpeg, shot.width, shot.height
c.click("desk", 640, 400)
c.type("desk", "ls -la")
c.key("desk", "Return")
c.screen_actions("desk", {"action": "scroll", "x": 640, "y": 400, "direction": "up"},
{"action": "wait", "ms": 300}, {"action": "key", "keys": "ctrl+c"})

Para um agente: rc mcp​

rc mcp é um servidor MCP por stdio, ligado a uma máquina. Registrado no Claude Code, ele dá ao agente as ferramentas de operar a VM:

claude mcp add rc -- rc mcp --name desk
FerramentaFaz
screenshotdevolve a tela como imagem e o tamanho em pixels
click, move, drag, type, key, scrollas ações da tabela acima, uma por chamada
actionsvárias ações em ordem, numa chamada
execroda um comando com sh -c e devolve o código de saída, o stdout e o stderr

Falha de uma ferramenta (máquina desligada, ação recusada, comando com saída diferente de 0) volta como resultado com isError, para o agente ler a mensagem e tentar outro caminho.

Medido com o Claude Code: pedimos "tire um screenshot, clique na aba bash · claude da barra de tarefas e confirme que a janela mudou". Ele tirou o print, achou a aba, clicou, viu a tela ainda sem mudar, esperou e confirmou a troca no segundo print.

Exemplo: desktop com Claude Code​

examples/desktop-claude é um desktop XFCE num runner.code: fundo preto, os atalhos Arquivos, Claude, Navegador e Terminal, Firefox como navegador padrão e o Claude Code instalado. O atalho Claude abre claude --dangerously-skip-permissions; a VM é a caixa de areia. O login do Claude entra por credencial: o guest só vê um placeholder.

examples/desktop-claude-agent manda tarefas a esse Claude de três jeitos, todos pelo SDK Go:

ModoComo a tarefa chegaVê na tela?
headlessclaude -p por exec; a resposta volta em JSONnão
guidigitada na janela do Claude com xdotool (qualquer texto)sim
keysdigitada pela API de tela (ScreenActions), sem nada instalado na VMsim

Cada modo pede ao Claude um arquivo com um conteúdo conhecido e confere o arquivo pela API: "funcionou" é o arquivo certo no disco da VM, não só uma resposta.