Repository navigation
Description
Objetivo e passagem de responsabilidade
Continuação do Monky Light (#119 e #306), sob responsabilidade de @JelielSantos. Esta issue concentra as pendências no corpo para que a continuidade não dependa de ler comentários antigos.
O objetivo permanece: comunicação com o menor trabalho de CPU possível, atendendo computadores fracos e quem precisa preservar desempenho para outras tarefas. Recursos opcionais devem consumir recursos apenas enquanto forem usados. Não substituir essa meta por tamanho de instalador ou por preferência de linguagem/framework.
Entrega do núcleo: #659 mesclado na main (d73a0ee). Release do repositório: v20.1.8-beta. Esta issue permanece no Backlog com @JelielSantos para continuidade.
Qualificação automatizada da passagem: CI completo aprovado no commit 0e594f0 em Windows x64, macOS Intel e Apple Silicon, incluindo componentes nativos e voz nativo/Chromium pelo servidor real. Registro: https://github.com/MonkyOrg/Monky/actions/runs/35094596177. Hardware real, macOS 12 e uso humano continuam pendentes abaixo.
O que já existe
- Núcleo C++ headless em
apps\light, sem Electron, Node ou webview residentes no cliente. Node participa da construção e dos cenários automatizados; o servidor continua sendo o Monky existente. - Autenticação Ed25519, perfil próprio, conexão WebSocket nativa, canais, voz P2P/SFU, mute/deafen, reconexão, moderação e encerramento.
- Windows x64 executado localmente, inclusive captura do microfone físico. Cenários com áudio sintético exercitam múltiplos participantes e interoperabilidade nativo/Chromium em P2P e SFU.
- Adaptadores macOS 12+ Intel/Apple Silicon, autorização assíncrona do microfone, Keychain e bundle preparados. A matriz de CI não equivale a avaliação com dispositivos reais nem comprova funcionamento na versão mínima do macOS.
- Constantes C++ geradas de
@monky/shared; não duplicar versão/tipos/limites manualmente. Use cliente e servidor da mesma branch e protocolo. - Construção, cenários e medição documentados nos READMEs PT/EN de
apps\light.
Ainda não existe aplicativo de bandeja nem instalador público do Light. A release normal do Monky não passa a conter downloads do Light só porque o código headless entrou no repositório.
Prioridade 1: assumir e concluir a qualificação do núcleo
- Reproduzir a construção e os cenários em Windows x64, macOS Intel e Apple Silicon; tratar falhas reais do CI sem desabilitar a cobertura para conseguir aprovar o PR.
- Exercitar macOS com hardware real e conferir compatibilidade com macOS 12: permissão concedida/negada/pendente, entrada já mutada, recepção enquanto o prompt está aberto, encerramento, Keychain bloqueado, identidade persistente e conexões
ws:///wss://. Confirmar que certificados não confiáveis continuam recusados. - Qualificar a identidade entre recompilações, assinatura e atualizações no macOS. O headless desabilita a UI do Keychain para o próprio processo; definir recuperação/autorização segura quando necessária, sem liberar acesso indiscriminado ao item nem substituir a identidade.
- Fazer chamada audível entre Light e o aplicativo Monky completo com duas pessoas, em P2P e SFU, incluindo vários participantes, diferentes dispositivos, mute/deafen, moderação, troca de sala, reconexão, expulsão e troca de topologia.
- Exercitar redes reais: LAN/internet, NAT, ICE/TURN, perdas e reconexões. Não usar servidor de produção para contornar incompatibilidade de protocolo.
- Implementar/expor seleção de entrada e saída de áudio e tratar troca/desconexão de dispositivo. O headless atual utiliza os dispositivos padrão do sistema; isso ainda não é uma interface completa de gerenciamento de áudio.
- Ampliar a avaliação de consumo: mesmo hardware, canal, participantes, política de áudio e condições para Light e Monky completo; incluir CPU, RAM, GPU, tráfego, tempo prolongado e recursos após sair/fechar opções. A amostra Windows atual é sintética e curta, não demonstra superioridade geral.
Amostra de referência local do núcleo em Release, não um limite de aceite: aproximadamente 13 MiB conectado sem chamada e 23–24 MiB durante voz sintética; aproximadamente 3% de um processador lógico durante a chamada. O valor não é o percentual normalizado pelo total de núcleos do Gerenciador de Tarefas.
Prioridade 2: interface mínima e uso cotidiano
- Implementar bandeja e pequenos painéis nativos para servidores, canais, estado da chamada, mute/deafen e dispositivos. Evitar renderização contínua e recursos escondidos ativos.
- Integrar atalhos e feedback de erros/estado, acessibilidade e localização de acordo com o idioma selecionado. Usar switches para liga/desliga e cards selecionáveis para escolhas exclusivas, nunca checkbox/radio nativo isolado.
- Definir os fluxos de identidade/configurações antes da distribuição: recuperação explícita, eventual importação/exportação e movimentação de perfil. Hoje corrupção/interrupção é recusada e mover um perfil macOS exige migração do vínculo do Keychain; nunca gerar outra identidade silenciosamente.
Recursos opcionais ainda não implementados
- Chat e mídias em interface sob demanda, com histórico paginado, cache limitado e sem download/reprodução desnecessários.
- Soundboard com reprodução nativa e acionamento por atalhos/comandos/controles mínimos; carregar sons conforme necessário e respeitar a escolha de receber sons de outros participantes.
- Miniapps em ambiente HTML criado e destruído sob demanda, reutilizando conteúdo e contratos existentes, com isolamento e ponte limitada de estado/ações. Não expor módulos nativos ou execução arbitrária ao HTML.
- Assistir a compartilhamentos por escolha explícita: assinar/decodificar somente os fluxos selecionados e interromper o consumo ao parar. As fábricas VP8 atuais servem à negociação de voz com ofertas que contêm vídeo; não constituem um player implementado.
- Hospedagem opcional pelo CLI existente em processo separado, com
MONKY_HOME, dados e portas próprios. Iniciar somente quando solicitado e conferir pré-requisitos antes de habilitar; não reescrever o servidor nem mantê-lo dentro do processo de voz.
Fora do escopo: sobreposição. Transmitir câmera/tela não faz parte do marco inicial; qualquer expansão precisa de decisão explícita, não deve ser assumida nesta continuidade.
Distribuição e manutenção ainda não implementadas
- Qualificar e definir manutenção/atualização do SDK WebRTC e libmediasoupclient nas três arquiteturas. O pin WebRTC M140 é uma referência de desenvolvimento, não aprovação automática para distribuição pública.
- Preservar licenças/notices e as verificações de integridade. A construção inclui
FieldTrialsda mesma revisão e uma correção estritamente verificada por hash para listas SDP opcionais ausentes; não remover essas proteções sem substituir a dependência e cobrir os casos. - Gerar artefatos/instaladores próprios do Light para Windows x64 e macOS Intel/Apple Silicon, com assinatura/notarização quando aplicável e versão obtida do pipeline (os metadados do bundle de desenvolvimento ainda têm versão provisória).
- Integrar o Light à mesma release do Monky, com nomes de download, identificação de aplicativo, perfis, instalação e metadados de atualização separados. Instalar/atualizar uma edição não pode substituir a outra.
- Implementar/qualificar atualização do Light e convivência com Monky instalado. Não promover beta para stable automaticamente.
- Atualizar site/documentação PT-BR e inglês para explicar diferenças entre as edições, disponibilidade por plataforma e servidor CLI + Light. Só anunciar downloads após os artefatos realmente existirem.
Como começar
- Atualizar a
main(o núcleo já foi mesclado), instalar as dependências normais do monorepo e consultarapps\light\README.md. - Preparar CMake 3.28–3.31; Windows requer Visual Studio 2022/Build Tools e Windows SDK. No Mac, usar ferramentas Xcode compatíveis com o pin de
apps\light\dependencies.json. - Executar os comandos abaixo na raiz. Os cenários de voz normais criam servidor/perfis descartáveis e usam fontes sintéticas, sem hardware físico.
- Para QA manual, iniciar servidor descartável da mesma branch e usar um perfil absoluto exclusivo com
--profile. A pasta pai deve existir. Nunca usar perfis/bancos/credenciais do Monky instalado. - Registrar resultados e atualizar este corpo conforme as pendências forem resolvidas, distinguindo construção, áudio sintético, hardware real e uso humano.
npm run build:light
npm run test:light:scripts
npm run test:light:native
npm run test:light:server
npm run test:light:voiceComandos opcionais, intencionais:
# Usa o microfone fisico; nao salva audio nem envia para um servidor externo.
npm run test:light:hardware
# Medicao atualmente implementada somente para Windows.
npm run measure:lightNo Mac, as construções ficam separadas por arquitetura:
npm run build:light -- --arch x64
npm run build:light -- --arch arm64
O executável de produção está em bin do diretório da arquitetura; no Mac, dentro de monky-light.app. Use --help para as opções e os READMEs para os comandos JSON de controle.
Achados do mega code review (#490)
LIGHT-1 a LIGHT-5 entraram no PR do review (Onda 8). Estes três ficaram fora por decisão. Nenhum muda protocolo.
- Escrita do stdout fora do loop do núcleo (LIGHT-4, item 3). Cada evento é escrito no stdout de forma síncrona, com
flush, dentro do loop do núcleo (apps/light/src/headless_application.cpp:228-238). No Windows, com o console em modo de seleção (QuickEdit), a escrita bloqueia: o loop para, não sai PING e a conexão cai. Não há tratamento deENABLE_QUICK_EDIT_MODEemapps/light/src.- Proposta: thread própria de saída com fila limitada, que junta os
media-statsatrasados. Alternativa barata no Windows: desligar o QuickEdit no console do Light. - Aceite: com o console em seleção por 60 s numa chamada, o Light continua conectado e não encerra.
- Proposta: thread própria de saída com fila limitada, que junta os
- Dividir o
voice_engine.cpp(LIGHT-6). O arquivoapps/light/src/media/voice_engine.cpptem 2.007 linhas e concentra fila de controle, runtime de áudio, P2P e SFU no mesmoImpl(VoiceEngine::Implem:319,Peerem:549,SfuListenerem:642). Sem um contrato comum, as políticas dos dois modos divergem em silêncio.- Proposta:
control_queue,audio_stack,p2p_mesh,sfu_sessione uma interfaceTopologyimplementada pelo P2P e pelo SFU; a API pública deVoiceEnginenão muda. - Quando: decisão do review, fazer quando vier vídeo ou uma terceira topologia.
- Aceite:
voice_engine_lifecycle_test.cpp,voice_engine_loopback_test.cpp,headless_interop.test.cjsebrowser_interop.test.cjspassam sem mudança de comportamento, nos três alvos do CI.
- Proposta:
- Primitivas de arquivo da identidade num lugar só (LIGHT-7). Leitura limitada, publicação por rename, handle com RAII e limpeza da seed estão em três lugares, já com verificações diferentes:
- handle com RAII:
apps/light/src/profile_identity.cpp:114-128(File) eapps/light/src/platform/identity_store_windows.cpp:37(Handle); requireOrdinaryFileexige link único (profile_identity.cpp:130-137);requireFileKindnão (identity_store_windows.cpp:90-98);- rename com guarda de tamanho do nome (
profile_identity.cpp:185-199,identity_store_windows.cpp:115-131) e sem guarda nowriteSettingsdo Windows (profile_identity.cpp:252-262); - limpeza da seed de três jeitos:
SecureZeroMemory(identity_store_windows.cpp:64e:75), laço volátil (apps/light/src/platform/identity_store_macos.mm:70) eOPENSSL_cleanse(apps/light/src/identity_key.cpp:35e:82); canonicalProfileduplicado:identity_store_windows.cpp:78eidentity_store_macos.mm:75;- Proposta:
platform/profile_files.{hpp,cpp}comOwnedHandle,ReadOrdinaryFile,WriteAll,PublishByRenameeSecureWipe, junto do checkbox de identidade/configurações acima. - Aceite:
profile_identity_test.cppeidentity_store_test.cpppassam no Windows e no macOS; link duplo e nome longo são recusados nos três caminhos.
- handle com RAII:
Activity
feat: adicionar núcleo nativo de voz do Monky Light (#659)
JelielSantos commentedon Sep 16, 2026
Seleção de dispositivos de áudio no Light: PR #661
Primeiro item de código da Prioridade 1: "Implementar/expor seleção de entrada e saída de áudio e tratar troca/desconexão de dispositivo".
O que foi implementado e por quê
Até aqui o núcleo headless usava sempre o microfone e a saída padrão do sistema. Agora ele permite escolher os dispositivos e continua funcionando quando eles mudam:
devices): mostra entradas e saídas comidename. A lista só é montada quando o comando é chamado, e o módulo de áudio é liberado logo depois, então fora de uma chamada nenhum dispositivo fica aberto. Isso segue a meta do Light de só gastar recurso quando algo está em uso.set-input/set-output): recebem oidlistado ounull, que segue o padrão do sistema.monky-light-settings.json, dentro do perfil, e não mexe na identidade. Se o arquivo estiver corrompido, o núcleo avisa e usa o padrão; a identidade continua intacta.audio-device-selectedcomfallback:true.IMMNotificationClient; macOS: listeners do Core Audio), sem ficar consultando a lista o tempo todo.kWindowsCoreAudio2), que reinicia o fluxo sozinho quando um dispositivo some. O "padrão" é o mesmo dispositivo padrão que o Monky completo (Chromium) usa.Principais arquivos:
apps/light/src/media/policy_audio_device.hpp,media/audio_devices.hpp,headless_application.cpp,profile_identity.cpp,platform/audio_device_watcher_{windows.cpp,macos.mm}emain.cpp.Testes já feitos (Windows x64)
build:light+test:light:scriptstest:light:nativetest:light:server+test:light:voice(2 cenários novos)test:light:hardware: microfone físico com troca real de entradameasure:lightComo testar (QA)
feat/light-audio-devices, rodenpm ci,npm run build:lighte suba um servidor descartável da mesma branch.{"command":"devices"}. Esperado: seus microfones e saídas aparecem comidename.{"command":"set-output","deviceId":"<id>"}. Esperado: o áudio passa a sair no dispositivo escolhido, sem cair a chamada.set-inpute tire ele da tomada durante a chamada. Esperado: aparecemaudio-devices-changedeaudio-device-selectedcomfallback:true, e a outra pessoa continua te ouvindo pelo microfone padrão.fallback:false).{"command":"mute","enabled":true}), troque o microfone. Esperado: a captura continua desligada.--profile. Esperado: a escolha foi mantida (devicesmostrainputDeviceId/outputDeviceId).Pendências deste item
measure:lightusa o dispositivo sintético).🤖 Generated with Claude Code
feat(light): medir consumo por árvore de processos e roteiro de QA ma…
feat(light): selecionar dispositivos de áudio e tratar troca/desconex…
JelielSantos commentedon Oct 4, 2026
💡 Como foi implementado & Decisões Técnicas
PR: #750 — primeiro item da Prioridade 2 (só a bandeja; painéis, atalhos e tela de conexão vêm depois).
--tray, o Monky Light ganha um ícone na área de notificação com estado, canais de voz, mute/deafen, dispositivos de áudio, reconectar e sair. O ponto delicado é a thread: oNSApplicationdo macOS só roda na thread principal, e o message pump do Windows só recebe mensagens na thread que criou a janela. Por isso, com--tray, a interface assume a thread principal e o laço do núcleo de voz vai para uma thread trabalhadora. Sem--traynada muda, então o caminho já qualificado continua idêntico.apps/light/src/ui/tray_model.hpp: estado e menu sem plataforma, coberto portest/tray_model_test.cpp.apps/light/src/ui/tray_windows.cpp/tray_macos.mm: só desenham (Win32 / Cocoa).apps/light/src/headless_application.*,main.cpp: inversão de threads com--tray.apps/light/src/protocol_session.*: novo eventochannels-changed(antes, criar/renomear/apagar canal mudava a lista em silêncio).apps/light/QA.md(seção 6), READMEs erelease-notes/658-light-tray.json.theme.css(identidade separada do Monky completo); marca de seleção nativa do menu no lugar de switch/card, que um menu de sistema não comporta; encerramento sem ícone fantasma nem processo órfão.🧪 Como testar (Guia de Validação para QA)
npm ci && npm run build && npm run build:light; servidor descartável da mesma versão (MONKY_HOMEe--datapróprios); um Monky completo conectado ao mesmo servidor.monky-light --tray --profile <pasta descartável> --server ws://<host>:<porta> --nickname QAapps/light/QA.md(19 passos).quitno stdin.stopped, sem processo órfão nem ícone fantasma.Validado: Windows (suítes automatizadas + QA manual da seção 6). Pendente: macOS em hardware real; o
tray_macos.mmé compilado pela primeira vez no CI deste PR.feat(light): adicionar interface de bandeja ao núcleo nativo
feat(light): adicionar interface de bandeja ao núcleo nativo (#750)
JelielSantos commentedon Nov 5, 2026
Indicador de fala e push-to-talk no núcleo (#885)
💡 Como foi implementado & Decisões Técnicas
speakingpara desenhar e o comandopttpara ligar ao atalho global.set_enabled(false)), mas o microfone continua capturando. Parar o dispositivo a cada toque atrasaria o começo da fala. Mute, deafen e restrições do servidor continuam valendo mesmo com a tecla pressionada.apps/light/src/media/speech_activity.hpp(novo): cálculo do nível, detector com histerese e medidor de captura.apps/light/src/media/policy_audio_device.hpp: intercala o medidor entre o dispositivo e o WebRTC.apps/light/src/media/voice_engine.*:AudioPolicy::transmit(controle do push-to-talk), amostragem de fala e eventospeaking.apps/light/src/headless_application.cpp: comandosset-input-modeeptt, eventospeakinge os camposinputMode/pushToTalkActivenostats.apps/light/src/profile_identity.*:inputModesalvo no perfil. Perfis antigos continuam emvoice_activity.anynem asserção forçada; preferência validada e gravada de forma atômica sob o lock do perfil. Não há mudança de protocolo.🧪 Como testar (Guia de Validação para QA)
npm run build:light; um--profilepróprio do Light. Para comparar, use também o Monky completo da mesma versão.monky-light.exe --profile … --server … --nickname Lighte entre num canal de voz com alguém usando o app completo.{"event":"speaking","sessionId":<o seu>,"speaking":true}ao falar efalsecerca de 0,4 s depois de parar. Quando a outra pessoa fala, o mesmo evento aparece com osessionIddela.{"command":"set-input-mode","mode":"push_to_talk"}. Resultado Esperado: você para de aparecer falando, e a outra pessoa deixa de te ouvir.{"command":"ptt","active":true}e fale, depois{"command":"ptt","active":false}. Resultado Esperado: a outra pessoa só te ouve entre os dois comandos, e o início da fala não é cortado.pttem modovoice_activity→command-error.pttativo, mute/deafen ou mute do servidor → não transmite, espeakingvai afalsena hora.statsmostrainputMode: push_to_talk.media-error, emeasure:lightsem diferença relevante de CPU em chamada.Automatizado (Windows x64):
test:light:nativecom 35 aprovados e 4 ignorados por plataforma;test:light:voicecom 29 aprovados e 1 ignorado, incluindo o novo cenário P2P/SFU com dois clientes nativos pelo servidor real. Ainda faltam chamada audível com pessoas, macOS real e medição de consumo.JelielSantos commentedon Nov 7, 2026
Volume por participante no núcleo (#892)
💡 Como foi implementado & Decisões Técnicas
sessionId, que identifica uma conexão (um dispositivo) de uma pessoa. Quem entra pelo celular e pelo PC pode ter um volume diferente em cada um, como no app completo.RemoteAudioSource::SetVolume), aplicado ao áudio decodificado antes da mixagem. Usar esse ganho evita colocar processamento próprio no caminho do áudio, e em 100% o custo é zero. O ganho é reaplicado sempre que aparece uma faixa nova daquela pessoa: P2P, consumo SFU, reconexão ou nova chamada.apps/light/src/media/voice_engine.*:JoinConfig::volumeseset_volume(), aplicados nas faixas P2P e nos consumidores SFU.apps/light/src/headless_application.cpp: comandoset-volume, eventoparticipant-volumeeuserVolumesnostats.apps/light/src/profile_identity.*: mapauserVolumesvalidado (0–200, sessão até 256 bytes, no máximo 256 entradas).release-notes/658-light-participant-volume.json.🧪 Como testar (Guia de Validação para QA)
npm run build:light; um--profilepróprio do Light. Entre numa chamada com outra pessoa, no Light ou no app completo.sessionIdda outra pessoa da lista de participantes (statsou eventos).{"command":"set-volume","sessionId":"…","volume":0}. Resultado Esperado: você deixa de ouvir essa pessoa, e os outros continuam ouvindo normalmente.volume: 200. Resultado Esperado: a voz dela fica claramente mais alta, sem chiado.stats.userVolumesmostra o valor.volume201, -1 ou 1,5 →command-error.volume: 100→ a entrada some deuserVolumes.media-error, e o volume nunca afeta o que os outros ouvem.Automatizado (Windows x64):
test:light:nativecom 35 aprovados e 4 ignorados por plataforma;test:light:voicecom 29 aprovados e 1 ignorado, incluindo o novo cenário P2P/SFU que mede a energia tocada em 0%, 100% e 200%.JelielSantos commentedon Nov 7, 2026
Bandeja com apertar para falar, fala e volume (#893)
O #893 junta o #885 e o #892, ambos fechados e apontando para ele, e liga tudo na bandeja. O funcionamento do núcleo está nos dois comentários anteriores; aqui fica o que muda na interface.
💡 Como foi implementado & Decisões Técnicas
Apertar para falareNa chamada(quem está falando e o volume de cada pessoa), e o ícone fica verde claro enquanto você fala.RegisterHotKeydo Windows só avisa quando a tecla é apertada, nunca quando é solta, e o apertar para falar precisa das duas coisas. Por isso o Light usa hooks de baixo nível (WH_KEYBOARD_LL). No macOS, usa o monitor global do AppKit, e o sistema só entrega teclas de outros aplicativos a quem foi autorizado em Acessibilidade.Mouse4/Mouse5. A tecla nunca é consumida e continua chegando ao jogo.--ptt-keye fica salva no perfil. Caps Lock e Num Lock ficam de fora porque segurá-los muda o estado deles no sistema.participants,input-modeepush-to-talk-key, e inclui o modo e a tecla noauthenticated.apps/light/src/push_to_talk_key.hpp(novo): tabela das teclas, com os códigos do Windows e do macOS.apps/light/src/ui/tray_model.hpp/tray_controller.hpp: estado, quando observar a tecla, deduplicação e comandos. Não dependem de plataforma e estão cobertos por testes.apps/light/src/ui/tray_windows.cpp/tray_macos.mm: menus, ícone e hook/monitor.apps/light/src/headless_application.cpp/profile_identity.*:--ptt-key,set-ptt-key,memberse os eventos novos.QA.md(§6.22–6.30) e nota de release.WM_DESTROY; nenhuma consulta paralela ao núcleo; textos PT/EN centralizados; nenhum checkbox nativo isolado (itens de menu nativos, como já documentado para a bandeja).🧪 Como testar (Guia de Validação para QA)
npm run build:light;monky-light.exe --tray --ptt-key F13 --profile … --server … --nickname QA. Se o teclado não tiver F13, use--ptt-key Mouse4ouRightControl.Apertar para falar. Esperado: o item fica marcado e mostra(tecla: F13).Apertar para falare fale. Esperado: o ícone fica verde claro enquanto você fala.Na chamadaenquanto a outra pessoa fala e escolha 0%, depois 200% e depois 100%. Esperado: ela aparece com(falando); o som some, fica mais alto e volta ao normal.apps/light/QA.md§6.22–6.30. Em resumo:pttno terminal;command-error;Automatizado (Windows x64):
test:light:nativecom 35 aprovados e 4 ignorados por plataforma;test:light:voicecom 31 aprovados e 1 ignorado. Também rodei um smoke manual do hook real com F13 injetado porkeybd_event: um aperto, mesmo com repetição, abre e fecha a transmissão, e o hook some ao sair da chamada. Ainda falta: qualificação em Mac real, chamada audível com pessoas emeasure:light.JelielSantos commentedon Nov 7, 2026
Abrir com duplo clique, sem linha de comando (#894, empilhado no #893)
💡 Como foi implementado & Decisões Técnicas
Trocar de servidor…na bandeja reabre a janela.--tray --profile … --server … --nickname …) e chama o mesmorunHeadlessqualificado. O que muda é que ninguém lê o console: comconsole = false, os eventos vão só para a bandeja, e a falta de leitor deixa de encerrar o núcleo, como acontece de propósito na linha de comando.ws(s)://: a chave do servidor já fica fixada no primeiro contato, e a senha fica só em memória. Para não virar um beco sem saída, quando o servidor recusa a senha ou o apelido a janela reabre no campo certo com a explicação, em vez de só um alerta na bandeja.runStandalone, que não depende de plataforma. Janela, perfil, alerta e núcleo entram como ganchos e são substituídos por dublês no teste.MONKY_LIGHT_PROFILEaponta para uma pasta descartável, para nunca tocar o perfil de uma instalação.apps/light/src/standalone.*(novo): fluxo de abertura, validação e mapeamento das recusas.apps/light/src/ui/connect_windows.cpp/connect_macos.mm/connect.hpp(novos): a janela, o aviso de perfil indisponível e o perfil padrão.apps/light/src/main.cpp: entrada sem argumentos,MONKY_LIGHT_PROFILEe console solto no Windows.apps/light/src/headless_application.*:console,passwordeaskAgain.apps/light/src/profile_identity.*:serverenicknamesalvos.Trocar de servidor….QA.md§6.31–6.39 e nota de release.Limitação conhecida: no Windows o executável continua sendo de console, então o duplo clique mostra um console por um instante antes de soltá-lo. Trocar para o subsistema GUI quebraria os cenários de console já qualificados (fechar o console faz logout, Ctrl+C e outros), por isso ficou para a etapa de distribuição.
🧪 Como testar (Guia de Validação para QA)
npm run build:light;$env:MONKY_LIGHT_PROFILE = "C:\Projetos\Monky-qa\light-app"(pasta descartável).apps\light\build\windows-x64\bin\monky-light.exesem argumentos. Esperado: a janela "Conectar a um servidor".ws://127.0.0.1:PORTA), escolha um apelido e conecte. Esperado: você entra no servidor, e a bandeja mostra os canais.apps/light/QA.md§6.34–6.39:Trocar de servidor…;Automatizado (Windows x64):
test:light:nativecom 36 aprovados e 4 ignorados por plataforma, incluindo o novomonky-light-standalone-test;test:light:voicecom 31 aprovados e 1 ignorado. Também rodei um smoke manual com o executável e a janela reais, preenchida via Win32: o endereço inválido foi recusado, o usuário entrou, o perfil guardou só servidor e apelido, e a reabertura conectou direto. O CI foi disparado à mão na branch, porque o workflow só roda para PRs contra amain.