shade-cli
O comando shade: cria, ensaia, testa, empacota e envia robôs da ShadeOne escritos com o
shade-sdk. O ciclo local roda sem conta; auth, deploy, releases e runs falam com a
plataforma pela chave de API, e shade mcp expõe o ciclo a um agente (Claude Code). Nunca importa o runtime no
próprio processo: o projeto roda em subprocesso, no venv dele (uv run na pasta), com o
shade-sdk que ele fixa.
uv tool install shade-cli
shade init cadastro-erp # starter de produção (--minimo para o enxuto)
cd cadastro-erp
uv run playwright install chromium
shade test # lint de contrato + pytest do projeto
shade run # ensaio: para antes de cada efetivar()
shade build # artefato determinístico em .shade/build/
Comandos
| Comando | Faz |
|---|---|
shade init <pasta> [--minimo] [--modelo lista|tarefa] [--editor vscode] [--id ID] [--nome NOME] [--sem-sync] |
o starter (produção por padrão) e o uv sync; nunca pergunta |
shade run [pasta] [--real [--sim]] [--entrada ARQ] [--item CHAVE] [--debug] [--trace] [--como-plataforma] [--headless] |
ensaio por padrão; --real pede TTY ou --sim |
shade test [pasta] |
lint (L1 a L8) e o pytest do projeto |
shade build [pasta] |
projeto.tar.gz (o mesmo pacote que a plataforma monta de uma gravação) e manifesto.json em .shade/build/; recusa o plugin de [plugins] que a imagem não tem, ou cujos hosts e credenciais o projeto não declara |
shade doctor [pasta] |
Python, uv, Chromium do Playwright, Docker e o shade-sdk do projeto |
shade docs [tópico] [--pasta P] |
a referência da versão do shade-sdk do projeto |
shade inspecionar <url> [--pagina Nome] [--pasta P] |
abre a página no Chromium (contexto efêmero, rede só em [hosts] permitidos), grava o que você clica e preenche, e propõe Pagina com Alvos já conferidos pelo diagnóstico do SDK; Enter no terminal encerra (--headless varre a página sem janela) |
Com a chave de API
| Comando | Faz |
|---|---|
shade auth login [--url URL] |
lê a chave sem eco (ou de uma linha do stdin, sem terminal), confere com a plataforma e guarda URL, chave e workspace no keyring do sistema |
shade auth status |
de onde vem a conta, o workspace e os escopos da chave, conferidos agora |
shade auth logout |
apaga a conta do keyring |
shade deploy [pasta] [--criar] [--nao-esperar] [--prazo S] |
confere /sdk/compat, monta o artefato em memória, envia e acompanha recebida → conferida → em ensaio → aguardando aprovação; o robo.id novo pede --criar sem terminal |
shade releases list [pasta] [--robo ID] [--limite N] |
as versões do robô |
shade releases show <id> [--esperar] [--prazo S] |
uma versão; com --esperar, acompanha como o deploy |
shade releases ensaiar <id> [--esperar] [--prazo S] |
repete o ensaio que caiu por infraestrutura ou esperava credencial |
SHADE_API_KEY e SHADE_URL vencem o keyring (o caminho do CI). A chave nunca é argumento,
nunca vai para arquivo e sai redigida de todo texto. http:// só para localhost.
O deploy é idempotente: o mesmo código dá o mesmo sha256, e o servidor devolve a release que
já existe. Se a CLI cair no meio, repetir shade deploy (ou shade releases show <id> --esperar) retoma a mesma versão. O prazo da espera (10 min) sai 6 com esse comando.
Tudo o que a CLI escreve num projeto fica em .shade/ (que se ignora sozinho no git): o
git status do projeto continua limpo depois de qualquer comando, menos o init.
Servidor MCP (shade mcp)
shade mcp [--pasta P] sobe um servidor MCP local, por stdio, sem porta, para o Claude Code (ou
outro cliente MCP) escrever, testar e enviar o robô com ferramentas observáveis. Vem num extra:
uv tool install 'shade-cli[mcp]'
cd cadastro-erp
claude mcp add shade -- shade mcp
O projeto é o shade.toml mais próximo do diretório em que o cliente sobe o servidor (ou
--pasta), fixo na sessão. A chave vem do keyring (shade auth login) ou de SHADE_API_KEY
e SHADE_URL. As ferramentas listadas são as que a chave e o projeto permitem: sem projeto, só
as da plataforma; sem chave, só as locais; chave só de leitura, nada que envie.
| Risco | Ferramentas | Precisa |
|---|---|---|
| local, sem rede | ler_manifesto, montar_artefato, rodar_lint |
o projeto |
| leitura da plataforma | listar_robos, ver_release, listar_runs, ver_run, listar_itens, ver_falha, buscar_eventos, ler_gravacao |
processes:read ou runs:read |
| navegação local | snapshot_da_pagina (árvore de acessibilidade e Alvos candidatos), conferir_seletor (em que elemento um Alvo cai) |
o projeto |
| executa código local | rodar_testes (shade test), rodar_ensaio (shade run em ensaio, headless) |
o projeto |
| envia sem pessoa | enviar_release (shade deploy, até aguardando_aprovacao) |
o projeto e releases:write |
Recursos: shade://sdk/referencia, shade://projeto/manifesto, shade://projeto/ultimo-ensaio,
shade://protocolo/runtime e shade://ferramentas/schema; cada um tem o equivalente em
ferramenta ou em shade docs.
O que o servidor garante, e o que não garante:
- Nenhuma ferramenta roda o robô em modo real, aprova nem ativa uma versão. O ensaio para
antes de cada
efetivar(); o--realfica no terminal, com você. - A resposta é o documento do
--json, comcomando= o nome da ferramenta, redigida pelo mesmo serializador: cada valor do.env, das credenciais declaradas, da chave e de variável com nome de segredo sai como[redigido], também URL-encoded e em base64. Nenhuma ferramenta lê.env, keyring, cookie ou o conteúdo de um trace; a captura da falha sai como caminho, tamanho e sha256. - O texto que veio do cliente (o log do run, a página, a gravação) chega só em
dados.cliente({"dados_do_cliente": true, "origem", "truncado", "conteudo"}), sem caractere de controle e com teto de 16 KiB e 200 linhas. É rótulo e teto, não barreira contra injeção: o modelo lê os mesmos tokens. Ids, estados e contagens ficam no topo, conferidos pela forma. Na gravação, todo valor digitado sai como[digitado]. - Caminhos são relativos ao projeto e presos a ele (
.., absoluto, symlink para fora e laço caem);.env*,.git/,*.peme.shade/credenciaissão recusados. Leitura até 256 KiB por arquivo, listagens até 500 entradas. - O navegador é sempre um Chromium headless num contexto novo (nunca o seu perfil, sem
service worker, sem download, sem o storage de outra chamada), com a rede só em
[hosts] permitidos, em HTTP e WebSocket, salto a salto nos redirecionamentos. IP privado, loopback, link-local,localhoste*.localsó passam listados por inteiro. O que a página pediu fora da lista volta embloqueados. Risco que fica: o DNS rebinding de um nome da lista.
CI
A mesma sequência em qualquer CI: instalar a CLI numa versão exata, shade test, shade deploy. A chave vem de SHADE_API_KEY, num segredo do CI e no env do job, nunca em argumento nem
em arquivo. Sem terminal a CLI nunca pergunta (--criar diz que o robô novo é de propósito), e o
--json põe o resultado no stdout. A release fica aguardando a aprovação de uma pessoa: o CI não
ativa nada. A saída 6 (prazo da espera) não é falha: a release segue no servidor e o
erro.recuperacao do JSON traz shade releases show <id> --esperar.
GitHub. A Action shade-deploy (.github/actions/shade-deploy/) roda essa sequência e devolve
os outputs release_id, project_sha256 e estado, lidos do JSON. O shade init já gera o
workflow com ela (job publicar, na branch padrão):
- uses: SamuelPerroni/shade-one/.github/actions/shade-deploy@cli-v0.1.6
env:
SHADE_API_KEY: ${{ secrets.SHADE_API_KEY }}
with:
versao-da-cli: "0.1.6" # exata; sem latest
# pasta: robos/cadastro # onde está o shade.toml (padrão: a raiz)
# url-da-plataforma: https://... # sem ela, vale o SHADE_URL ou o padrão da CLI
O repositório do ShadeOne é privado: para outro repositório usar a Action, libere o acesso em Settings > Actions > General > Access do repositório do ShadeOne.
GitLab.
publicar:
image: ghcr.io/astral-sh/uv:python3.14-bookworm
script:
- uv tool install shade-cli==0.1.6
- export PATH="$(uv tool dir --bin):$PATH"
- uv sync --locked
- shade test --json
- shade deploy --json --criar | tee deploy.json
- python3 -c "import json; print(json.load(open('deploy.json'))['dados']['estado'])"
variables:
SHADE_API_KEY: $SHADE_API_KEY # variável mascarada do projeto
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Azure Pipelines.
- script: |
pip install uv
uv tool install shade-cli==0.1.6
export PATH="$(uv tool dir --bin):$PATH"
uv sync --locked
shade test --json
shade deploy --json --criar
displayName: Publicar o robô
env:
SHADE_API_KEY: $(SHADE_API_KEY) # variável secreta: mapeie-a em env, o Azure não a expõe sozinho
Nos dois, o código de saída é o do shade: 1 gate reprovado, 3 chave, 6 prazo (retome com o
comando do JSON), 7 rede. Trate o 6 como aviso se quiser o comportamento da Action.
--json e códigos de saída
Com --json, um documento no stdout; progresso e logs no stderr, redigidos:
{"versao": 1, "comando": "build", "ok": false, "dados": {},
"erro": {"codigo": "lint_reprovado", "mensagem": "L3 robo.py:1 import fora do lock: yaml",
"recuperacao": "corrija o que o lint aponta (shade test mostra a lista inteira)"}}
| Código | Significado |
|---|---|
| 0 | ok (inclusive o deploy que acha a release existente, e a release que espera uma pessoa: aprovar ou preencher o cofre) |
| 1 | gate, lint ou teste reprovou; release reprovada na conferência ou no ensaio (e o defeito da própria CLI, com erro.codigo = "erro_interno") |
| 2 | uso inválido ou shade.toml inválido; robô novo sem --criar |
| 3 | chave ausente, recusada ou sem o escopo (401/403) |
| 4 | conflito de estado (409): ensaiar uma release reprovada, a release arquivada |
| 5 | incompatibilidade de versão: o shade-sdk do projeto não é o da imagem, ou o /sdk/compat não casa |
| 6 | o prazo da espera acabou; a release segue no servidor (erro.recuperacao traz a retomada) |
| 7 | rede ou servidor indisponível (5xx, 429) |
| 130 | interrompido (Ctrl-C, SIGTERM ou SIGHUP: o robô em andamento morre junto) |
Lint de contrato
| Regra | Efeito | O quê |
|---|---|---|
| L3 | reprova | import de pacote fora do lock da imagem e dos plugins da imagem que o projeto declara em [plugins] |
| L7 | reprova (avisa no run) |
credencial lida sem declaração, ou o valor dela chegando a goto, ao HTTP do Playwright, a shade_sdk.http ou a arquivo |
| L8 | reprova | entrada ausente ou inválida (main.py, [robo] classe, arquivo que não compila) |
| L1 | avisa | subprocess, os.system, os.exec*, os.fork, ctypes, multiprocessing |
| L2 | avisa | socket, http.client, urllib.request, requests, httpx, aiohttp |
| L4 | avisa | eval, exec, compile, import dinâmico |
| L5 | avisa | caminho literal fora do workdir |
| L6 | avisa | clique ou envio em alvo com cara de submissão fora de efetivar() |
O lint não é controle de segurança: quem isola o robô é o executor da plataforma.
Desenvolvimento
cd cli && uv sync
uv run pytest -q -m "not starter" # rápido
uv run pytest -q -m starter # os starters de verdade: uv sync, Chromium, ~45 s
src/shade_cli/imagem.py é a cópia do requirements.lock da imagem do run:
backend/tests/test_hu294_artefato_da_cli.py a confere, e confere que o artefato do
shade build passa no ler() do backend.
Metadata
Release files for shade-cli 0.1.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shade_cli-0.1.10-py3-none-any.whl | Python 3 | none | any | Details |
Release files / shade_cli-0.1.10-py3-none-any.whl
| Download URL | shade_cli-0.1.10-py3-none-any.whl |
|---|---|
| Size | 159.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2f51f68db68f1f191ddf91d0ee614d803b8b0b903b9b27b5721a60db1d54a0fa
|
|
BLAKE2b-256 checksum How to use checksums |
7ee19c99c3f7f0af3ffa2d33756e22ebb3128a0813879755b1bf67ed3ea5efc5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for shade-cli 0.1.10
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.