Projetos e SDK
Como organizar projetos Node.js e Python para rodar no browserMate via upload de ZIP, e como usar o Mini SDK para acessar variáveis e conectores do processo.
Quando usar projetos externos
Use projetos ZIP em vez de código inline quando:
- O código tem múltiplos arquivos ou módulos separados
- Precisa de dependências específicas (bibliotecas não disponíveis no runtime padrão)
- O código é extenso e difícil de manter no editor inline
- Quer manter o código em Git e enviar cada release como uma versão do projeto
- A lógica é compartilhada entre múltiplas etapas ou processos
Como enviar um projeto

- Empacote o projeto como um arquivo .zip (a raiz do ZIP deve conter os arquivos do projeto diretamente, não uma pasta extra).
- No Menu, acesse a opção Codebase.
- Clique em + Novo Projeto e preencha o formulário com Nome, Descrição, Linguagem e Arquivo de entrada (entrypoint).
- Selecione a linguagem (Node.js ou Python) e informe o entry point (arquivo principal a executar).
- Faça o upload do ZIP e salve.
- Na etapa de código customizado, selecione Modo de Execução: Projeto e escolha o projeto enviado em Projeto de Script.
Versões do projeto
Cada upload de ZIP gera uma versão nova do projeto, numerada em sequência. A versão mais recente é a que os agentes executam, marcada como Em uso, e as anteriores continuam guardadas.
No card do projeto, abra o histórico para ver a lista completa. Cada linha traz o número da versão, a data do upload, o tamanho do pacote e o entry point que valia naquele momento.
O histórico guarda as 10 versões mais recentes. Acima disso, os pacotes mais antigos são descartados para não acumular armazenamento indefinidamente.
Alterações que não mexem no código, como editar nome ou descrição do projeto, não geram versão.
main.js (ou index.js). Para Python use main.py. Esse arquivo deve importar e executar toda a lógica do projeto.
Estrutura Node.js
meu-projeto/
├── main.js ← entry point
├── package.json ← dependências
├── src/
│ ├── processador.js
│ ├── validador.js
│ └── formatador.js
└── utils/
└── helpers.js
{
"name": "meu-projeto-bm",
"version": "1.0.0",
"main": "main.js",
"dependencies": {
"axios": "^1.6.0",
"xlsx": "^0.18.5",
"xml2js": "^0.6.2"
}
}
const bm = require('./browsermate');
const Processador = require('./src/processador');
(async () => {
// Ler parâmetros de entrada. As chaves usam o formato {variavel}
const arquivo_base64 = bm.get('{arquivo_entrada}');
const tipo = bm.get('{tipo_documento}', 'NFe');
// Processar
const proc = new Processador(tipo);
const resultado = await proc.processar(arquivo_base64);
bm.bmLog('[OK] Documento processado: ' + resultado.numero);
// Gravar resultado e encerrar (obrigatório)
bm.done(resultado.status, {
'{numero}': resultado.numero,
'{valor_total}': resultado.valor,
'{status}': resultado.status,
'{dados}': resultado.dados
});
})();
Estrutura Python
meu-projeto/
├── main.py ← entry point
├── requirements.txt ← dependências pip
├── src/
│ ├── __init__.py
│ ├── processador.py
│ └── validador.py
└── utils/
├── __init__.py
└── helpers.py
pandas==2.1.0
openpyxl==3.1.2
requests==2.31.0
PyPDF2==3.0.1
lxml==4.9.3
import browsermate as bm
from src.processador import Processador
def main():
# Ler parâmetros de entrada. As chaves usam o formato {variavel}
arquivo_b64 = bm.get('{arquivo_entrada}', '')
tipo = bm.get('{tipo_documento}', 'NFe')
# Processar
proc = Processador(tipo)
resultado = proc.processar(arquivo_b64)
bm.bmLog('[OK] Documento processado: ' + resultado['numero'])
# Gravar resultado e encerrar (obrigatório)
bm.done(resultado['status'], {
'{numero}': resultado['numero'],
'{valor_total}': resultado['valor'],
'{status}': resultado['status'],
'{dados}': resultado['dados']
})
if __name__ == '__main__':
main()
API do Mini SDK
O Mini SDK é o módulo browsermate injetado automaticamente pelo runtime no diretório de execução. Para scripts inline, o import é adicionado antes do seu código; para projetos, importe no topo do entry point:
// Node.js
const bm = require('./browsermate');
# Python
import browsermate as bm
{variavel}, com chaves, como chave no globalData. Use esse exato formato em bm.get() e em bm.done(). A credencial é a exceção: bm.secret('CHAVE') recebe a chave crua, sem chaves em volta, porque ela não é variável do processo.
| API | Node.js | Python | Descrição |
|---|---|---|---|
| Ler variável | bm.get('{var}', default) |
bm.get('{var}', default) |
Lê uma variável do processo, no tipo que ela tem no fluxo: texto, número, lista (array / list) ou objeto (dict no Python). Retorna default se não encontrada. |
| Todas as variáveis | bm.globalData |
bm.globalData |
Objeto/dict somente leitura com todas as variáveis. Use bm.done() para escrever. |
| Ler credencial | bm.secret('CHAVE') |
bm.secret('CHAVE') |
Lê uma credencial do Cofre de Senhas pela chave, sem chaves em volta. A chave pode ser montada em tempo de execução. Chave inexistente levanta erro listando as disponíveis, em vez de devolver vazio. |
| Log de execução | bm.bmLog(mensagem) |
bm.bmLog(mensagem) |
Registra mensagem nos logs oficiais de execução (visível no Control Room) |
| Finalizar script | bm.done(result, updates) |
bm.done(result, updates) |
Obrigatório. Grava variáveis de volta ao processo e encerra. Sem essa chamada, nenhum dado é retornado ao fluxo. |
| ID do processo | bm.get('{processID}') |
bm.get('{processID}') |
ID do processo atual. É uma variável de sistema comum, não uma env var. Veja Manipulação de Variáveis. |
| ID da instância | bm.get('{instanceID}') |
bm.get('{instanceID}') |
ID único desta execução |
Credenciais do Cofre no seu código
bm.secret() lê uma credencial do Cofre de Senhas. A chave vai crua, sem chaves em volta, porque a credencial não é variável do processo. E ela pode ser montada em tempo de execução, o que atende o agente que atende vários clientes, cada um com a sua credencial:
// Node.js
const senha = bm.secret('ERP.SENHA');
const porCliente = bm.secret('ERP.SENHA.' + bm.get('{cliente}'));
# Python
senha = bm.secret('ERP.SENHA')
por_cliente = bm.secret('ERP.SENHA.' + bm.get('{cliente}'))
{vault.CHAVE} não funciona dentro de um script: nada substitui {...} ali, e por isso existe o bm.secret(). Chave inexistente levanta erro listando as disponíveis, em vez de devolver vazio e produzir um erro de autenticação incompreensível na outra ponta. E nunca devolva uma credencial em updates: updates grava no fluxo, e ali ela viraria uma variável comum, visível como qualquer outra.
Escrever variáveis de volta ao processo
bm.done(result, updates) é a única forma de gravar variáveis. O parâmetro updates usa o mesmo formato {variavel} como chave:
// Node.js
bm.done('processado', {
'{numero_nf}': 'NF-001',
'{valor_total}': '1500.00',
'{status}': 'aprovado'
});
# Python
bm.done('processado', {
'{numero_nf}': 'NF-001',
'{valor_total}': '1500.00',
'{status}': 'aprovado'
})
Cada valor é gravado como é: texto, número, lista ou objeto vão direto em updates, e bm.get() devolve do mesmo jeito.
bm.done() passam a aparecer sozinhas nos seletores de variáveis das etapas seguintes. Num projeto ZIP isso não acontece, porque o código fica no arquivo enviado e não no cadastro da etapa. As variáveis são gravadas normalmente na execução: escreva o nome com chaves, como {numero_nf}, nos campos que precisarem dele. Da mesma forma, o aviso de erro de sintaxe não confere o código do projeto.
Desenvolvimento e teste local
Para testar um script ou projeto sem precisar executar pelo runtime completo, crie o arquivo input.mock.json na raiz do projeto com as variáveis que o processo forneceria:
{
"globalData": {
"{arquivo_entrada}": "JVBERi0x...",
"{tipo_documento}": "NFe",
"{empresa_id}": "ACME"
}
}
Ao rodar o script localmente (node main.js ou python main.py), o SDK detecta que está fora do runtime (variável de ambiente BM_INPUT ausente) e lê o input.mock.json. O resultado é gravado em output.json e exibido no console. O bm.bmLog() também imprime no console em modo local.
input.mock.json não é enviado ao runtime quando você faz upload do ZIP, e é ignorado na execução real. Use-o livremente para testes sem risco.
input.mock.json que a plataforma gera. Para testar na sua máquina com um valor no lugar de um campo sensível, digite você mesmo um valor de teste no seu input.mock.json, e nunca o dado real.
Credenciais num Projeto ZIP
Vale o mesmo do código Inline: bm.secret('CHAVE'), descrito em Credenciais do Cofre no seu código. O SDK é injetado na execução, então o projeto não declara nem instala nada para isso.
Os segredos chegam ao projeto pelo mesmo caminho do globalData, mas num arquivo à parte, que é apagado no fim da execução. Eles nunca entram em input.mock.json, justamente porque esse arquivo fica no disco depois que a execução acaba.
secrets.mock.json na raiz do projeto, no formato {"ERP.SENHA": "valor-de-mentira"}, e o SDK lê dele. Nunca coloque a credencial de verdade nesse arquivo, e nunca o envie no ZIP.
bm.secret(), ou uma etapa Conectar Serviços, onde a credencial fica no conector e é decifrada no servidor no momento da chamada.
updates
updates grava no fluxo. Uma credencial ali deixa de ser segredo e vira variável comum do processo, visível como qualquer outra. Use o valor dentro do script e descarte.
Gerenciamento de dependências
Os projetos rodam sobre Node.js 24 ou Python 3.14, conforme a linguagem escolhida. Declare dependências compatíveis com essas versões. Veja Versões de Node.js e Python.
Num Projeto ZIP as bibliotecas são declaradas em arquivo, na raiz do projeto:
| Linguagem | Arquivo | Como o código usa |
|---|---|---|
| Node.js | package.json | Os pacotes ficam na pasta do projeto, disponíveis via require(). |
| Python | requirements.txt | As bibliotecas vão para um ambiente Python isolado do projeto, disponíveis via import. |
pandas==2.1.0) para garantir reprodutibilidade entre execuções.
O runtime instala isso sozinho ao receber o projeto, sem passo manual. Quando essa instalação acontece, quanto ela demora, como rodar sem acesso aos repositórios de pacotes e por que cada projeto fica isolado dos demais está tratado em um só lugar, porque vale igual para Código Inline e para Projetos ZIP: Visão Geral → Como as dependências são processadas.
