Um plugin chega ao Novus CRM por um de dois caminhos. Os dois usam o mesmo protocolo e o mesmo manifesto de permissões. Muda só quem serve os arquivos.

Desenvolvimento local

Para desenvolver, você não precisa de túnel nem de publicar nada. Sirva os arquivos na sua máquina e cadastre esse endereço como um plugin só seu.
1

Sirva a pasta do plugin

Qualquer servidor estático serve, como python3 -m http.server 8080. Se o plugin usa <script type="module">, o servidor precisa responder com Access-Control-Allow-Origin: *. Veja Isolamento.
2

Cadastre o endereço local

Em Configurações > Plugins, cadastre um plugin com Endereço próprio e a URL local, por exemplo http://localhost:8080/index.html.
3

Ligue Só eu vejo este plugin

Com a opção ligada, os outros usuários da conta não carregam o plugin enquanto você desenvolve. Sem ela, avise a equipe ou use uma conta de teste, porque todo mundo tentaria carregar um endereço que só existe no seu computador.
O servidor local só entrega os arquivos. Para conferir eventos e chamadas de API, abra o plugin dentro do Novus CRM. Se o navegador pedir acesso à rede local, permita. Mantenha o novus-plugin.json atualizado desde o começo do desenvolvimento. O plugin com endereço próprio ainda não passa pela checagem de permissões, mas o pacote hospedado passa, e uma chamada que funcionava na sua máquina pode ser recusada depois de publicar. Veja Superfícies não declaradas somem.

Hospedagem no Novus CRM

Cadastre o plugin como Hospedado no Novus e escolha o endereço (slug). O slug vira o subdomínio do plugin e segue estas regras:
  • De 3 a 40 caracteres: letras minúsculas, números e hífen, começando e terminando com letra ou número.
  • Sem dois hífens seguidos, porque -- separa o número da versão.
  • Único entre todas as contas do Novus CRM.
  • Fixo depois da primeira publicação.
Alguns nomes são reservados, como www, api, app, admin, docs e suporte. Cada conta tem até 20 plugins hospedados e 500 MB somando todas as versões guardadas.

Estrutura do pacote

O pacote é um .zip com o site do plugin já pronto. O Novus CRM não roda npm install nem build: envie a pasta de saída, como dist/, e não o projeto.
O novus-plugin.json fica na raiz. Se o .zip tiver uma única pasta de topo com o manifesto dentro, essa pasta vira a raiz. Os campos do manifesto estão em Manifesto e permissões. Um exemplo completo de pacote:
novus-plugin.json
O nome é livre. A versao segue semver e não pode repetir entre envios do mesmo plugin: para enviar de novo, aumente o número. Reenviar exatamente o mesmo pacote não cria versão nova, devolve a que já existe.

Extensões aceitas

O pacote aceita só arquivos de site estático: html htm css js mjs json map webmanifest txt md svg png jpg jpeg gif webp avif ico woff woff2 ttf otf wasm Um único arquivo com outra extensão recusa o pacote inteiro, e a mensagem lista todos eles. Arquivo sem extensão também é recusado. Isso pega .env, .gitignore, LICENSE e Dockerfile, os casos mais comuns de quem zipa a pasta do projeto. Estes itens são ignorados sem erro: a pasta __MACOSX/ e os arquivos .DS_Store e Thumbs.db.

Limites

O pacote também é recusado quando tem caminho absoluto, .., barra invertida, link simbólico ou dois arquivos com o mesmo nome variando só maiúsculas e minúsculas. Todos os problemas voltam de uma vez, não um por envio.
Segredo nunca vai no pacote. Qualquer pessoa com o endereço de uma versão consegue baixar os arquivos dela. Guarde chaves e tokens como variável da conta e use secretRefs no apiRequest, como em Chamando uma API externa. O Novus CRM procura padrões comuns de credencial nos arquivos de texto, como X-Chave-Api, Basic, sk-, ghp_, AKIA e chaves privadas, e mostra um aviso com arquivo e linha. O aviso não bloqueia o envio, então não conte com ele.

Versões

Cada envio vira uma versão numerada e imutável. Nada muda para a equipe até você publicar.
1

Envie o pacote

Em Configurações > Plugins, abra a ação Versões do plugin e arraste o .zip, ou use a CLI e a API de publicação. O Novus CRM valida o pacote e cria a versão n.
2

Pré-visualize

Pré-visualizar faz só você ver a versão nova dentro do Novus CRM, no seu navegador. A faixa Você está pré-visualizando fica visível até você clicar em Encerrar pré-visualização. Os outros usuários continuam na versão ativa.
3

Publique

Publicar troca a versão ativa. Quem usa o plugin passa a ver a versão nova no próximo carregamento. Um iframe já aberto não recarrega sozinho.
4

Reverta, se precisar

Voltar para esta versão publica de novo uma versão anterior. É uma troca de ponteiro, sem reenviar arquivos.
Cada versão tem dois endereços: A publicação chega à borda em até um minuto. As permissões aprovadas da versão nova passam a valer no proxy no mesmo prazo. Enquanto isso, a tela mostra a versão como Publicando. O Novus CRM guarda as 20 versões mais recentes de cada plugin, mais a ativa e qualquer versão instalada por outra conta pelo catálogo. As demais são apagadas. Um plugin excluído sai do ar na hora, e os arquivos são apagados 30 dias depois.

Aprovação de permissões

Ao publicar ou reverter, o Novus CRM compara as permissões da versão com as aprovadas na versão ativa.
  • Iguais ou menores: publica direto.
  • Com qualquer item novo: só um administrador ou o responsável pela conta publica, depois de revisar a lista e marcar Revisei e autorizo estas permissões. O Novus CRM registra quem aprovou e quando.
Na primeira publicação não existe versão aprovada, então qualquer permissão declarada conta como nova. A API de publicação nunca aprova permissão: ela recusa com 409 e devolve o link da tela de aprovação.

Cache

Você não precisa de hash no nome dos arquivos para a versão nova aparecer. Um plugin.js sem hash funciona. Pelo mesmo motivo, não aponte para o endereço de uma versão numerada esperando que ele mude: ele nunca muda.

Respostas de erro

Com "spa": true, um caminho sem extensão que não existe no pacote, como /configuracoes, devolve a entrada. Um arquivo com extensão que não existe, como /assets/velho.js, continua em 404. O plugin fica na raiz do subdomínio, então um build do Vite com base: "/" funciona sem ajuste.

Isolamento e o que muda no seu código

Todo plugin roda num iframe com sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox", sem allow-same-origin. Isso vale para os dois caminhos de publicação. O navegador trata o plugin como uma origem opaca (null), sem relação com a sessão do Novus CRM. O plugin hospedado recebe a mesma restrição no próprio documento. Toda resposta de novusplugin.com traz estes cabeçalhos, inclusive quando alguém abre a URL direto numa aba:
O novusplugin.com é um domínio separado do Novus CRM de propósito. A página O domínio novusplugin.com explica o motivo. O que isso muda para quem escreve o plugin:
  • Sem armazenamento no navegador. localStorage, sessionStorage, IndexedDB e document.cookie lançam erro de segurança. Guarde o estado em memória e envolva em try/catch qualquer biblioteca que tente usar esses recursos. O que precisa persistir vai para um sistema pelo apiRequest.
  • Script de módulo precisa de CORS. Um <script type="module">, que é o que todo build do Vite gera, faz uma requisição CORS com Origin: null. A hospedagem já responde Access-Control-Allow-Origin: *. No endereço próprio, configure o seu servidor para mandar o mesmo cabeçalho, senão o script não executa. Isso vale também para fontes.
  • fetch direto para outra API quase sempre falha. A requisição sai com Origin: null, que a maioria das APIs recusa, e qualquer chave no código ficaria exposta. Use o apiRequest, que passa pelo proxy do Novus CRM.
  • Sem alert, confirm e prompt. O sandbox não libera diálogos nativos. Use openAlert para avisos e faça confirmações na interface do plugin.
  • Sem download iniciado pelo plugin. O sandbox não libera downloads.
  • Sem câmera, microfone, geolocalização, pagamento e USB no plugin hospedado.
  • Uma origem por plugin. openPage e openModal só aceitam URL absoluta na mesma origem do cadastro, e ignoram endereço de outro site. Monte a URL a partir da página atual, com new URL("tela.html", location.href).href, e não com caminho relativo, porque quem resolve o caminho é a página do Novus CRM.
  • Fora de busca. O plugin hospedado não é indexado por buscadores e não envia Referer nas requisições.