Modo servidor
ai serve expõe o roteador como uma API compatível com a OpenAI. Ele usa os mesmos logins, chaves, contas, cooldowns e fallbacks do CLI.
ai serve # http://127.0.0.1:8080/v1
ai serve --host 0.0.0.0 --port 9000 --max-concurrent 4 --timeout 600
ai serve keys [list | add NAME | remove NAME]
API de administração
Desligada por padrão. Configura um servidor em execução sem acesso ao shell.
ai serve admin-keys add ops # chave própria, mostrada uma única vez
ai serve --enable-admin
curl -H "Authorization: Bearer omni-admin-..." http://127.0.0.1:8080/admin/health
| Rota | Função |
|---|---|
GET/PUT /admin/config |
Modelos, ordem de fallback (fallback_order) e routing |
GET/POST /admin/keys, DELETE /admin/keys/{id} |
Chaves dos provedores. O id é openrouter ou openrouter.work |
GET /admin/accounts |
Contas, login e cooldown |
GET/POST /admin/api-keys, DELETE /admin/api-keys/{nome} |
Chaves dos clientes de /v1 |
GET /admin/health |
Estado do servidor |
POST /admin/accounts/{conta}/login |
Inicia o login por assinatura: devolve o link e o id da tentativa |
POST /admin/accounts/{conta}/login/complete |
Conclui o login |
DELETE /admin/accounts/{conta}/login |
Cancela, com o id em X-Omni-Login-Attempt |
- Chaves separadas: uma chave de
/v1nunca abre/admin, e vice-versa. Sem chave de administração,--enable-adminnão inicia. - Rede: de fora desta máquina, exige HTTPS por proxy reverso, que precisa definir
X-Forwarded-Proto.--admin-allow-httpdesliga essa checagem. Ela só evita exposição por engano: quem alcança a porta direto consegue forjar o cabeçalho, então deixe a porta aberta só para o proxy. No Docker, a porta publicada conta como acesso externo. - Segredos: as respostas mostram só o final das chaves de provedores. A exceção é a chave de cliente criada, mostrada uma única vez.
- Campos protegidos: modo de permissão, caminhos de binários e
config_dirretornam 403. - Cuidado: esta chave pode ligar modelos pagos (
openrouter_paid) e trocar chaves de provedores. No Docker, useSMART_AI_ADMIN=1.
Login pela API. {conta} é primary e as outras contas Claude, codex, cursor, grok, ou codex.NOME para uma conta extra. Uma tentativa vale 10 minutos e cada conta aceita uma por vez (409).
| Ferramenta | Como concluir |
|---|---|
| Claude Code | Abra o link e envie {"attempt_id": "...", "code": "..."} com o código obtido |
| Codex, Grok | Abra o link, digite o user_code e envie {"attempt_id": "..."}. A resposta é 202 até concluir |
| Cursor | Abra o link e envie {"attempt_id": "..."}. A resposta é 202 até concluir |
O Antigravity e as ferramentas só com chave retornam 400. Os códigos e tokens não aparecem em respostas nem em logs. A exceção é o user_code, que precisa ser digitado no navegador.
Escrita em paralelo
Nos modos de escrita, os pedidos rodam em paralelo, até --max-concurrent, sem mexer na pasta original.
| Pasta com git | Pasta sem git | |
|---|---|---|
| Onde trabalha | Worktree em uma branch omni/<id>, a partir do último commit |
Cópia da pasta, até 1 GB |
| Resultado | Commit na branch, informada em X-Omni-Branch. O merge é seu |
Cópia em ~/.local/share/smart-ai/runs/, informada em X-Omni-Copy |
| Continuar | Envie X-Omni-Branch no pedido seguinte |
Envie X-Omni-Copy no pedido seguinte |
| Limpeza | O worktree sai ao final. A branch fica até você apagar | A cópia fica até você apagar |
- Sem alterações: a branch ou a cópia nova é descartada e o header não vem. Em streaming, o header sai no início e pode apontar para algo descartado.
- Falha ou timeout: o que já foi feito é mantido, e o header vem na resposta de erro. Depois de uma queda do servidor, as alterações são salvas no próximo início.
- Concorrência: dois pedidos na mesma branch ou cópia recebem 409. Só branches
omni/podem ser continuadas. - Fila: acima do limite, o pedido espera até
--queue-waitsegundos (60), com até--queue-sizena fila (16). Só então recebe 429. - Limitações: alterações sem commit e arquivos ignorados pelo git, como
node_modules, não entram no worktree. O isolamento vale para arquivos: portas, bancos e outros serviços usados pelos comandos ainda podem colidir, e um comando pode escrever fora da pasta. A memória também conta: cada pedido é um processo de CLI. Os comandos git do próprio servidor não rodam hooks nemcore.fsmonitordo repositório. Filtros do git, como os do Git LFS, continuam rodando: sirva só repositórios em que você confia.
ai serve work --workdir PASTA # lista
ai serve work remove ID --workdir PASTA # mostra; com --yes, apaga
ai serve work prune --older-than 30 --workdir PASTA # também --merged; com --yes, apaga
Sem --yes, nada é apagado. O comando só alcança branches omni/ e cópias do Omni. Um item em uso, ou cópia alterada há menos de 15 minutos, é mantido.
Como serviço
ai serve --install-service [--host H] [--port P] [--mode M] [--workdir PASTA]
ai serve --service-status
ai serve --uninstall-service
Inicia sozinho e reinicia em caso de falha, com launchd no macOS e systemd de usuário no Linux. No Windows, em containers e em WSL sem systemd, o comando avisa e não instala; rode ai serve manualmente.
- Segredos: o arquivo do serviço não guarda chaves. Sem chave salva, a instalação cria uma e a mostra uma única vez. Fora de
127.0.0.1, exige uma chave salva antes. - Ambiente: só
PATHe as pastas do Omni passam para o serviço. Chaves que existem só no shell não passam: salve comai --key. A instalação avisa quais são, e também quais CLIs não encontrou. - Pasta e modo: o padrão é
plan, em~/.local/share/smart-ai/serve.--mode yoloe sobrescrever um arquivo que o Omni não gerou exigem--yes. - Logs:
~/.cache/smart-ai/serve.log, com rotação, eserve.out.log, limpo a cada instalação. - Linux: para iniciar no boot sem sessão aberta, rode
loginctl enable-linger $USER. A instalação avisa, mas não faz isso sozinha. - Atualização:
ai --updatereinicia o serviço. Em instalações sem git, reinstale o serviço depois.
Uso da API
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8080/v1", api_key="omni-...")
client.chat.completions.create(model="omni-auto", messages=[{"role": "user", "content": "Olá"}])
No primeiro início, o servidor cria uma chave e a mostra uma única vez. Só o hash fica salvo. Em container, também é possível definir SMART_AI_SERVER_KEY. Não existe modo sem autenticação.
| Rota | Função |
|---|---|
POST /v1/chat/completions |
Resposta completa ou streaming (SSE) |
GET /v1/models |
omni-auto, que usa o roteador, e omni/<ferramenta> para cada ferramenta configurada |
| Header da resposta | Significado |
|---|---|
X-Omni-Provider |
Ferramenta que respondeu |
X-Omni-Usage |
reported (informado pelo provedor) ou estimated (estimado). Não aparece em streaming |
X-Omni-Ignored-Params |
Parâmetros recebidos e ignorados |
Segurança:
- Por padrão, as ferramentas rodam no modo
plan, em uma pasta temporária, seja qual for oSMART_AI_MODE. - Para criar, editar e rodar comandos:
ai serve --mode edits|auto|yolo --workdir PASTA. A pasta é obrigatória e nada pede aprovação. Cada pedido trabalha isolado. Quem tem a chave pode alterar a pasta e, com comandos, alcançar os logins das contas. - Somente leitura não impede ler arquivos. Quem tem a chave pode pedir a uma ferramenta que leia arquivos desta máquina. MCPs configurados também podem estar disponíveis. Trate a chave como acesso à sua conta.
- O padrão é
127.0.0.1. Para expor em rede, use--hoste HTTPS com proxy reverso. - Acima do limite de conexões (128 ou mais, conforme o sistema), a resposta é 503. O início de cada pedido precisa chegar em até 30 segundos. Em exposição pública, use também os limites por origem do proxy.
- Quem compartilha uma instância compartilha as mesmas contas e assinaturas. Verifique os termos de cada provedor.
Limitações:
- Quando o modo do servidor nega uma ação e a ferramenta encerra com erro, como faz o Grok, a resposta é
403 action_not_allowed. temperaturesó vale em OpenRouter, DeepSeek e Grok via API. Nos CLIs, é ignorada e aparece emX-Omni-Ignored-Params.max_tokensé aproximado nos CLIs (cerca de 4 caracteres por token).usageé estimado quando o provedor não informa o consumo.- Cada pedido pode consumir bastante cota, porque os CLIs de agente carregam o próprio contexto. No Codex, um simples "OK" já usou cerca de 19 mil tokens de entrada.
- Nos CLIs, o streaming entrega a resposta de uma vez, ao final. Só o OpenRouter envia pedaços durante a geração, exceto quando há dados pessoais mascarados.
- O fallback é transparente até o primeiro trecho da resposta. Se o provedor falhar depois disso, o cliente recebe um erro.
tools/function calling só nos provedores de API (OpenRouter, DeepSeek e Grok via chave). As ferramentas nunca são executadas pelo servidor: a chamada volta para o seu código. Outros modelos retornam 400, porque os CLIs de agente não têm como devolver a chamada, a não ser com--tool-emulation. Comomni-auto, a ordem é OpenRouter, DeepSeek e Grok. No OpenRouter, semopenrouter_paid, só modelos gratuitos. Em streaming, a chamada chega de uma vez, ao final. Argumentos fora do schema fazem o servidor tentar o próximo provedor; só o núcleo do JSON Schema é checado (type,required,enum,properties,items,additionalProperties).functions(formato antigo) retorna 400.- Experimental:
ai serve --tool-emulation(SMART_AI_TOOL_EMULATION=1no Docker) emulatoolsem Claude Code e Grok CLI, com as ferramentas próprias deles desligadas. O modelo escreve a chamada em JSON, o servidor valida e tenta de novo uma vez, o que gasta mais cota. Só entra quando não há provedor de API. Codex e Cursor ficam de fora, porque não dá para desligar as ferramentas deles. O modelo pode errar mais que no modo nativo, os dados pessoais não são mascarados, e o conteúdo do cliente pode imitar o formato. - Imagens retornam erro 400.
- O prompt chega às ferramentas como argumento de linha de comando e fica visível na lista de processos da máquina. O limite é de 100 KB por pedido.
- Perfis Claude legados, que trocam o arquivo de credenciais, limitam o servidor a um pedido por vez.
Este texto vem do README do projeto. Ver o original no GitHub