Documentação
Companheiro móvel
Um cliente iOS para um desktop emparelhado, com fixação de certificados, revisão de projetos e sessões de assistente compartilhado.
Traduzido automaticamente do inglês. Ver o original em inglês
Plataforma e recursos
apps/mobile é um aplicativo Expo SDK 57 usando React Native 0.86.3, React 19.2, Expo Router, NativeWind, Reanimated e Gesture Handler. Seu transporte de rede fixo suportado é implementado em Swift pelo local companion-net módulo. O identificador do pacote iOS é com.cadcamfun.mobile.
O telefone lê projetos, analisa o histórico e pull requests, despacha fluxos de trabalho, recebe atividades e interage com sessões de assistente em execução na área de trabalho emparelhada. O desktop continua a possuir o banco de dados, a computação geométrica, as credenciais do provedor de IA e a execução do fluxo de trabalho. Uma área de trabalho acessível e em execução é necessária para essas operações.
Use uma versão nativa do iOS
Expo Go não inclui companion-net e não consegue se conectar a uma área de trabalho emparelhada. As solicitações nativas falham quando o módulo de fixação não está disponível. O substituto de desenvolvimento web usa busca no navegador e armazenamento local; ele não fornece fixação de certificado nativo.
Estrutura do aplicativo
O Expo Router separa as telas de conexão das guias de aplicativos autenticados. app/connect.tsx lida com entrada de emparelhamento, app/pair.tsx aceita links diretos e app/(app) contém Projetos, Assistente, Atividade e Configurações. As telas do projeto incluem painéis de visão geral/histórico/pull/fluxo de trabalho, detalhes de commit e revisão de pull-request.
components/: controles reutilizáveis, painéis de projeto, transcrição do assistente e UI de conexão.lib/client.ts: cliente digitado em um transporte injetado; Os tipos de API são importados sem o tempo de execução do servidor.lib/pairing.ts,native.ts, estorage.ts: validação de emparelhamento, seleção de transporte e credenciais persistentes.lib/sse.ts,live-events.ts, echat-log.ts: análise de fluxo, assinaturas ao vivo e estado de transcrição.modules/companion-net/: Módulo Expo e implementação HTTPS fixada em Swift.
Escopo de transporte e gateway
O gateway complementar é separado do ouvinte de loopback do sidecar. Está desativado por padrão. Ativá-lo inicia um servidor HTTPS em 0.0.0.0, usando a porta 8443, a menos que outra porta válida seja selecionada. O serviço persiste em seu estado habilitado e o retoma após a inicialização do sidecar. O firewall do desktop e a rota da rede devem permitir que o telefone alcance esse ouvinte.
O telefone envia um token de dispositivo revogável. O gateway armazena seu hash SHA-256, verifica o método/caminho da solicitação em relação a uma lista de permissões e encaminha as solicitações aceitas para a API de loopback usando o token de desktop. O telefone não recebe esse token de inicialização. Autenticação inválida retorna 401, rotas negadas retornam 403, autenticação com falha repetida pode retornar 429 e um upstream indisponível retorna 502.
Allowed families include project reads; version-control, pull-request, and workflow endpoints; project events; notifications; assistant status/models/agents; streaming chat; queue/abort; permissions; and session state/control. Files, credentials, plugins, billing, gateway administration, and GitHub remote/push/pull endpoints are excluded.
O acesso ao dispositivo inclui operações de gravação
O gateway atualmente permite solicitações de confirmação, ramificação, checkout e restauração de controle de versão, bem como revisão e mesclagem de solicitações pull. Os tokens de dispositivo têm como escopo a política de rota do gateway; eles não são uma função somente leitura ou uma restrição por projeto. Revogue um telefone perdido ou não confiável nas configurações complementares do desktop.
O gateway limita as tentativas de autenticação com falha: após dez tentativas de portador com falha de um endereço IP em 60 segundos, outras solicitações desse endereço recebem HTTP 429 até que a janela expire (apps/sidecar/src/services/companion-gateway.ts).
Sessões do assistente e mensagens off-line
O acompanhante envia solicitações de bate-papo para POST /api/ai/chat/stream e analisa eventos enviados pelo servidor. Um identificador de sessão permite a continuação da conversa na área de trabalho. O cliente pode direcionar ou enfileirar mensagens, abortar uma curva, responder perguntas e responder a solicitações de permissão. Configure o provedor de modelo na área de trabalho; o telefone consulta o status da IA antes de usar.
Se um fluxo terminar sem um terminal done ou error quadro e não foi cancelado, o cliente relata uma conexão incompleta. O transporte nativo fecha seu identificador de fluxo no cancelamento e na conclusão. A decodificação nativa preserva escalares UTF-8 incompletos entre blocos de bytes.
As mensagens do assistente que não podem ser enviadas podem entrar em uma fila persistente. Ele retém no máximo 50 entradas, eliminando as mais antigas quando cheias, e tenta novamente em primeiro plano e recuperação de conectividade. As entradas com falha permanecem na fila. Esta fila armazena mensagens para execução posterior; não é uma cópia offline da geometria do projeto ou um tempo de execução de IA offline.
Construir e executar
bun install
bun run --cwd apps/mobile test
bun run --cwd apps/mobile typecheck
bun run --cwd apps/mobile build
bun run --cwd apps/mobile ios
bun run --cwd apps/mobile ios:release --device O comando build exporta o pacote JavaScript do iOS para apps/mobile/dist; não produz um IPA. Os comandos de execução do iOS requerem macOS, Xcode e CocoaPods. A instalação do dispositivo também requer um dispositivo confiável, modo de desenvolvedor e uma equipe de assinatura da Apple configurada para o projeto Xcode gerado. native:generate executa o pré-construído Expo para iOS sem instalar pods. Para o trabalho diário, start (alias dev) executa o empacotador de cliente de desenvolvimento Expo, ios:dev inicia no localhost e abre o simulador iOS, ios:build compila o aplicativo de depuração sem iniciar um empacotador e web inicia o alvo web da Expo. O test script é executado bun test test dentro apps/mobile.
.github/workflows/ci.yml define verificação de tipo complementar, testes lógicos e exportação de JavaScript. .github/workflows/ios.yml gera o projeto Xcode, cria um aplicativo simulador de versão não assinado, emparelha-o com um sidecar ativo, verifica a listagem de projetos autenticados e captura uma captura de tela. Essas verificações configuradas não estabelecem aceitação de dispositivo físico, assinatura, TestFlight ou provedor real.
Armazenamento e segurança do dispositivo
No iOS nativo, as credenciais de emparelhamento são armazenadas no Expo SecureStore. A entrada de emparelhamento usa WHEN_UNLOCKED_THIS_DEVICE_ONLY. Entradas de fila offline e marcadores vistos por sessão também usam o SecureStore. O substituto do navegador usa armazenamento local para desenvolvimento.
O aplicativo consulta a capacidade biométrica e usa expo-local-authentication para autenticação local, com fallback de senha do sistema habilitado. A UI biométrica não altera as permissões de rota do lado do servidor do gateway. A revogação do dispositivo é controlada pela área de trabalho emparelhada.
O desktop mantém seu certificado de gateway e chave privada no diretório complementar de dados do aplicativo; a chave é escrita com permissões exclusivas do proprietário. A substituição desse certificado altera a impressão digital fixada e requer um novo emparelhamento confiável.
Fluxo de trabalho de revisão de projeto
- Abra Projetos e escolha um projeto de desktop.
- Inspecione seu status de trabalho, ramificações, confirmações, solicitações pull ou execuções de fluxo de trabalho.
- Abra um commit para revisar sua comparação ou abra uma solicitação pull para revisar as alterações e enviar um veredicto.
- Use as ações disponíveis de mesclagem/fechamento ou expedição de fluxo de trabalho quando apropriado para o projeto.
- Abra o Assistant com o contexto do projeto selecionado para continuar ou iniciar uma sessão de IA.
Todas as alterações são executadas na área de trabalho. A interrupção da rede, a autorização revogada ou uma área de trabalho interrompida impedem o trabalho de novos servidores; as mensagens do assistente enfileiradas localmente são repetidas posteriormente.
Interface do cliente
CompanionClient aceita um Transport with JSON request and text-stream methods. It exposes project reads; VCS status, history, diff, commit, branch, checkout, and restore; pull requests and reviews; workflow runs and dispatch; AI status/models/agents; session control; notifications; and project event streams.
Falhas JSON tornam-se CompanionError com um status e uma mensagem. O streaming de bate-papo e eventos ao vivo usa o analisador SSE compartilhado para que limites arbitrários de partes da rede não se tornem limites de eventos do aplicativo. Os testes injetam transportes sem importar módulos nativos ou executar um simulador.
Emparelhar um telefone
- Na área de trabalho, abra Configurações e seu painel complementar para celular ou
/settings/companione ligue o gateway. - Crie um emparelhamento de dispositivo nomeado e exiba o código QR. O link contém candidatos a host HTTPS, um token de dispositivo e a impressão digital do certificado.
- No iOS, escaneie o código QR ou cole o
cadcamfun://pairlink. Permita acesso à rede local quando solicitado. - O aplicativo valida o link, compete com os candidatos anfitriões anunciados e mantém a primeira conexão de saúde bem-sucedida.
- Confirme se o dispositivo aparece nas configurações da área de trabalho. Use a ação de revogação da área de trabalho para remover seu acesso.
Os candidatos de host incluem endereços IPv4 privados e de rede de sobreposição adequados; o gateway anuncia no máximo seis endereços descobertos. O ouvinte atual é IPv4, portanto, endereços IPv6 descobertos não são anunciados. Uma VPN ou rede sobreposta já deve fornecer roteamento para uso fora da rede local; o emparelhamento não cria um retransmissor ou VPN.
Fixação de certificado
A implementação Swift usa efêmero URLSession instâncias com um delegado de confiança. Ele aceita um certificado folha somente quando o hash SHA-256 de seus bytes DER for igual à impressão digital do emparelhamento. As solicitações exigem HTTPS, os redirecionamentos são recusados e os chamadores nativos nunca fazem downgrade silenciosamente para o transporte do navegador.
A confiança do certificado, portanto, vem do link de emparelhamento. Trate o código QR e o link colado como credenciais. Um link com origem ou impressão digital inesperada deve ser substituído por meio do fluxo de emparelhamento de desktop, em vez de ignorar a validação TLS.
Diagnosticar uma conexão
- Sem transporte nativo: instale um desenvolvimento iOS ou versão de lançamento contendo o módulo local.
- Nenhum host acessível: verifique permissão de rede local, firewall de desktop, endereço anunciado, porta de gateway e roteamento de rede.
- 401: o token do dispositivo é inválido ou revogado; crie um novo emparelhamento.
- 403: o endpoint está fora da lista de permissões complementar.
- Incompatibilidade de certificado: emparelhe novamente com o certificado de desktop atual.
- Assistente indisponível: verifique a configuração do provedor de desktop e se o sidecar está em execução.
Configurações do telefone e estado local
| Contexto | Padrão | Comportamento |
|---|---|---|
| Aparência | Sistema | Escolha Sistema, Claro ou Escuro. O sistema segue o iOS até ser substituído. |
| Tátil | Habilitado | Os tiques de seleção e o feedback de sucesso/erro têm alternâncias separadas. |
| Bloqueio de identificação facial | Desabilitado | A ativação requer biometria registrada disponível e uma autenticação de sistema bem-sucedida. |
| Bloquear no fundo | Habilitado | Quando o bloqueio biométrico está ativado, mover para inativo/em segundo plano bloqueia o aplicativo emparelhado. |
| Conexão de área de trabalho | Emparelhamento atual | Mostra o endereço e o prefixo da impressão digital; tocar na linha da área de trabalho verifica novamente a conectividade. |
| Mensagens na fila | Zero | Mostra mensagens pendentes do assistente enquanto a área de trabalho está inacessível. |
As preferências da UI ficam no AsyncStorage em cadcamfun.ui.v1; credenciais de emparelhamento, mensagens em fila e marcadores de sessão vista usam SecureStore. O armazenamento de UI lembra o projeto selecionado e a última sessão por projeto para uma área de trabalho emparelhada. A alteração da identidade da área de trabalho limpa o mapeamento de projeto/sessão lembrado para que não seja reutilizado em um servidor não relacionado.
O coordenador de bloqueio ignora as telas de inicialização e conexão e mostra uma sobreposição de desbloqueio em tela inteira para um aplicativo emparelhado. Ele não suspende sessões do lado do servidor nem revoga um token. O desemparelhamento remove o emparelhamento salvo do telefone e retorna ao Connect; a revogação da área de trabalho é a ação separada que impede que o token seja autenticado novamente.
Exemplo de administração de gateway
Essas solicitações são executadas na API de loopback de desktop com seu token de inicialização, e não no gateway telefônico. Use uma instância de desenvolvimento isolada. Substitua a porta de exemplo pela porta do seu evento ready e forneça o token desse processo.
CADCAM_URL=http://127.0.0.1:43111
CADCAM_TOKEN=local-development-token
curl --fail-with-body "$CADCAM_URL/api/companion" -H "Authorization: Bearer $CADCAM_TOKEN"
curl --fail-with-body -X POST "$CADCAM_URL/api/companion/enable" -H "Authorization: Bearer $CADCAM_TOKEN" -H 'Content-Type: application/json' -d '{"port":18443}' A resposta de status contém enabled, um anulável port, um anulável fingerprint, a sugestão host, e devices. O serviço aceita portas explícitas de 1024 até 65535. Uma porta já em uso falha ao iniciar o ouvinte; escolha outra porta e gere novamente o endereço de emparelhamento.
curl --fail-with-body -X POST "$CADCAM_URL/api/companion/pair" -H "Authorization: Bearer $CADCAM_TOKEN" -H 'Content-Type: application/json' -d '{"name":"Development iPhone","host":"192.168.1.20"}' Substitua o host de exemplo por um endereço para o qual o telefone possa rotear. A criação de emparelhamento bem-sucedida retorna HTTP 201 com um dispositivo id e uma credencial link. O nome cortado deve conter 1–60 caracteres. Digitalize/cole esse link de forma privada; não o copie em um relatório de bug. A criação do token não ativa automaticamente o gateway.
Para remover o acesso, envie DELETE /api/companion/devices/<id> usando autorização de desktop. Para interromper todo o novo tráfego de gateway, envie POST /api/companion/disable. Os hashes dos dispositivos são verificados em cada solicitação, portanto, os tokens revogados são recusados nas solicitações subsequentes.
Recuperação sem perder contexto
- Toque na linha de conexão da área de trabalho em Configurações para tentar novamente a integridade dos hosts anunciados.
- Se apenas um caminho de rede falhar, verifique a rota Wi-Fi/hotspot/VPN correta e gere novamente um código QR após as alterações de endereço da área de trabalho.
- Se a autorização falhar, revogue o dispositivo obsoleto na área de trabalho e crie um novo emparelhamento.
- Se apenas um fluxo do assistente falhar, reabra a sessão do projeto e inspecione seu estado antes de reenviar a mesma instrução.
- Inspecione a contagem de mensagens na fila antes de enviar um prompt duplicado; as operações na fila serão repetidas mais tarde.
A fila off-line possui retenção limitada e reproduz cada operação pendente por meio de uma nova solicitação de bate-papo que transporta o identificador de sessão armazenado, quando disponível. Não é uma garantia de entrega exatamente única. Uma queda na rede após a execução do servidor pode deixar o cliente incerto sobre a conclusão; inspecione o estado resultante do projeto/sessão antes de repetir uma alteração.
Âncoras de implementação: app/(app)/settings.tsx, components/layout/AppLockCoordinator.tsx, lib/store.ts, lib/server.tsx, e lib/offline-queue.ts.
Lista de permissões de gateway
O telefone se comunica com o desktop apenas através do gateway complementar, que admite esse conjunto fixo de rotas e as encaminha para a API de loopback:
| Métodos | Padrão de caminho | Propósito |
|---|---|---|
| GET | /api/health | Verificação de acessibilidade. |
| GET | /api/projects, /api/projects/:id | Lista e cabeçalho do projeto. |
| OBTER, POSTAR | /api/projects/:id/vcs/* (exceto remote, push, pull), /pulls/*, /actions/* | Histórico, pull requests, revisões e execuções de fluxo de trabalho. A sincronização do GitHub permanece na área de trabalho porque precisa do token GitHub do proprietário. |
| GET | /api/projects/:id/events | Transmissão SSE ao vivo de histórico, fluxo de trabalho e atividade do assistente; o único canal push do telefone. |
| OBTER, POSTAR, COLOCAR, EXCLUIR | /api/notifications, /mark-all-read, /:id | Leia, marque como lida e exclua notificações. |
| GET | /api/ai/status, /models, /agents | Descoberta do assistente. |
| POST | /api/ai/chat/stream, chat/abort, chat/queue, permission/reply, sessions/state, sessions/control | Sessões de assistente. |
Como a lista é fixada no sidecar, um recurso que precisa de outra rota (por exemplo, upload de arquivo ou push do GitHub) é um recurso somente para desktop até que a lista seja alterada.
O que o telefone armazena
| Item | Armazenar | Notas |
|---|---|---|
| Origens do gateway | SecureStore | O host preferido mais os alternativos do link de emparelhamento. |
| Token do dispositivo | Loja Segura, WHEN_UNLOCKED_THIS_DEVICE_ONLY | Nunca sai do dispositivo, exceto como cabeçalho de suporte para o gateway. |
| Impressão digital do certificado | SecureStore | Comparado com o certificado do gateway em todas as conexões. |
| Fila offline, marcadores de sessão | SecureStore | Mensagens compostas off-line e o último estado visto das sessões. |
Em uma versão de desenvolvimento de navegador, os mesmos dados voltam para o armazenamento local, que não é criptografado. Use-o apenas com combinações descartáveis.
Telas
| Arquivo de rota | Tela |
|---|---|
app/index.tsx | Entrada: sem emparelhamento → tela de emparelhamento; emparelhado → projetos |
app/connect.tsx, app/pair.tsx | Emparelhamento: escaneie o código QR com a câmera ou cole o link |
app/(app)/projects/index.tsx | Lista de projetos |
app/(app)/projects/[id].tsx | Um projeto |
app/(app)/projects/[id]/commit/[oid].tsx | Um commit e suas mudanças |
app/(app)/projects/[id]/pulls/[number].tsx | Uma solicitação pull: revisar e mesclar |
app/(app)/assistant.tsx | Bate-papo do assistente |
app/(app)/activity.tsx | Atividade ao vivo |
app/(app)/settings.tsx | Configurações de conexão e dispositivo |
As rotas usam convenções de arquivo do Expo Router: (app) é um grupo que compartilha um layout sem adicionar um segmento de URL e [id] é um parâmetro.
Fila do assistente off-line
Quando não é possível acessar a área de trabalho, uma mensagem digitada no assistente fica na fila do telefone em vez de ser perdida:
| Regra | Valor |
|---|---|
| Onde | SecureStore, então um aplicativo eliminado mantém a fila |
| O que está armazenado | projectId, text, opcional sessionId, agent, mode (geral, cad, cam, gcode, percurso da ferramenta; padrão geral) e queuedAt |
| Capacidade | 50 entradas; os mais velhos são descartados primeiro |
| Quando drena | Quando o aplicativo retorna ao primeiro plano com um emparelhamento armazenado |
| Como | Cada entrada abre um novo turno de streaming e lê até done; um error evento ou uma transição em segundo plano o mantém na fila |
| Opinião | Um brinde: "Mensagem na fila enviada" ou "N mensagens na fila enviadas" |
Somente falhas de conectividade enfileiram uma mensagem. Um envio conta como off-line quando o cliente relata o status 0 ou o texto do erro menciona uma falha de rede, tempo limite, conexão recusada ou algo semelhante; Erros de HTTP e de negócios ("solicitação para… falhou com…") são mostrados em vez de enfileirados.