Pular para o conteúdo principal

Empacotamento e Implantação

Empacotamento para Implantação

Quando seu app estiver pronto, você precisa empacotá-lo em um arquivo .zip para fazer o upload. O arquivo ZIP deve seguir convenções específicas de estrutura e incluir um arquivo manifest.json obrigatório.

O Arquivo manifest.json (Obrigatório)

Todos os apps devem incluir um arquivo manifest.json no diretório assets/. Este arquivo contém os metadados do seu app:

{
"app": {
"type": "lua",
"name": "my-app",
"title": "My App",
"version": "1.0.0",
"description": "Uma breve descrição do que seu app faz",
"dependencies": [
{
"name": "other-app",
"version": ">=1.0.0"
}
]
},
"contact_fields": [
{
"type": "STRING",
"name": "customer_id",
"display": "ID do Cliente",
"private": false
}
],
"journeys": [
{
"name": "Jornada de Boas-vindas",
"file": "journeys/welcome.md",
"description": "Dá boas-vindas a novos usuários"
}
],
"media_assets": [
{
"asset_path": "images/logo.png",
"filename": "logo.png",
"content_type": "image/png",
"description": "Logotipo da empresa"
}
],
"skills": [
{
"name": "get_weather",
"file": "my-app/skills/get_weather.lua"
}
]
}

Metadados do App (Obrigatório)

Campos obrigatórios:

  • app.type: O tipo do app (ex: "lua" para apps Lua)
  • app.name: O nome do seu app (deve corresponder ao nome do arquivo Lua principal sem extensão)
  • app.title: O título de exibição para seu app (mostrado na UI da Turn)
  • app.version: String de versão semântica (ex: 1.0.0, 2.1.3, 1.0.0-beta)
  • app.description: Uma breve descrição da funcionalidade do seu app

Campos opcionais:

  • app.dependencies: Um array de apps que devem estar instalados para que seu app funcione. Cada dependência deve especificar:
    • name: O nome exato do app requerido
    • version: Uma restrição de versão (ex: >=1.0.0, ~>2.1, 1.0.0)

Comportamento de dependências:

  • Se dependencies não for especificado ou for um array vazio [], a plataforma assume que seu app não tem dependências e pode ser instalado independentemente
  • Quando dependências são especificadas, a plataforma valida que todos os apps requeridos estão instalados com versões compatíveis antes de permitir a instalação
  • Entradas de dependência inválidas (sem name ou version) são silenciosamente ignoradas

Campos de Contato (Opcional)

O array contact_fields define campos personalizados que seu app adicionará aos contatos. Esses campos são criados automaticamente quando o app é instalado usando turn.manifest.install().

"contact_fields": [
{
"type": "STRING",
"name": "customer_id",
"display": "ID do Cliente",
"private": false
},
{
"type": "BOOLEAN",
"name": "is_premium",
"display": "Membro Premium",
"private": false
},
{
"type": "NUMBER",
"name": "loyalty_points",
"display": "Pontos de Fidelidade",
"private": true
}
]

Propriedades do campo:

  • type (obrigatório): Tipo do campo - "STRING", "BOOLEAN", "NUMBER", ou "DATE"
  • name (obrigatório): Nome interno do campo (minúsculas, sublinhados permitidos)
  • display (obrigatório): Rótulo legível mostrado na UI
  • private (obrigatório): true para ocultar de usuários não-admin, false para mostrar a todos

Notas:

  • Campos são criados automaticamente durante a instalação do app
  • Campos podem ser inscritos para notificações de mudança usando turn.app.set_contact_subscriptions()
  • Use private: true para dados sensíveis como registros médicos, informações financeiras ou identificadores pessoais

Jornadas (Opcional)

O array journeys define templates de jornada que seu app fornece. Estes são arquivos markdown no seu diretório assets/ que definem fluxos de conversação.

"journeys": [
{
"name": "Jornada de Boas-vindas",
"file": "journeys/welcome.md",
"description": "Dá boas-vindas a novos usuários e coleta informações básicas"
},
{
"name": "Fluxo de Pagamento",
"file": "journeys/payment.md",
"description": "Processa pagamentos com integração Stripe"
}
]

Propriedades da jornada:

  • name (obrigatório): Nome de exibição para a jornada na UI da Turn
  • file (obrigatório): Caminho para o arquivo markdown dentro do diretório assets/
  • description (obrigatório): Breve descrição do que a jornada faz

Notas:

  • Arquivos de jornada são importados automaticamente durante a instalação ao usar turn.manifest.install()
  • Jornadas podem chamar de volta para seu app usando a função DSL app()
  • Arquivos markdown de jornadas suportam a sintaxe DSL completa de jornadas da Turn.io

Ativos de Mídia (Opcional)

O array media_assets define imagens e outros arquivos de mídia incluídos com seu app. Estes são enviados e disponibilizados para uso em jornadas e mensagens.

"media_assets": [
{
"asset_path": "images/welcome-banner.jpg",
"filename": "welcome-banner.jpg",
"content_type": "image/jpeg",
"description": "Imagem de banner de boas-vindas"
},
{
"asset_path": "images/logo.png",
"filename": "logo.png",
"content_type": "image/png",
"description": "Logotipo da empresa"
},
{
"asset_path": "documents/terms.pdf",
"filename": "termos-de-servico.pdf",
"content_type": "application/pdf",
"description": "Documento de termos de serviço"
}
]

Propriedades do ativo de mídia:

  • asset_path (obrigatório): Caminho para o arquivo dentro do diretório assets/ do seu app
  • filename (obrigatório): Nome a usar ao fazer upload (pode ser diferente de asset_path)
  • content_type (obrigatório): Tipo MIME (ex: "image/jpeg", "image/png", "application/pdf")
  • description (obrigatório): Descrição do propósito do ativo

Tipos de conteúdo suportados:

  • Imagens: image/jpeg, image/png, image/gif, image/webp
  • Documentos: application/pdf
  • Vídeos: video/mp4, video/3gpp
  • Áudio: audio/mpeg, audio/ogg, audio/aac

Notas:

  • Arquivos de mídia são enviados para o armazenamento de mídia da Turn durante a instalação
  • Arquivos enviados recebem URLs únicas que podem ser usadas em mensagens e jornadas
  • Use turn.assets.read() para acessar o conteúdo binário de ativos para upload

Skills (Opcional)

O array skills define skills de agentes IA que seu app fornece. Apps podem incluir tanto Skills de Código (arquivos Lua) quanto Skills de Conhecimento (arquivos Markdown).

"skills": [
{
"name": "get_weather",
"file": "my-app/skills/get_weather.lua"
},
{
"name": "lookup_order",
"file": "my-app/skills/lookup_order.lua"
},
{
"name": "politica_devolucao",
"file": "my-app/knowledge/politica_devolucao.md"
}
]

Propriedades de skill:

  • name (obrigatório): O nome da skill usado ao anexar a agentes IA
  • file (obrigatório): Caminho para o arquivo de skill dentro do pacote do app (.lua para skills de código, .md para skills de conhecimento)

Skills de Código (.lua):

  • Executam código Lua quando chamadas pelo agente IA
  • Devem incluir anotações LDoc definindo parâmetros e tipos de retorno
  • Têm acesso à API completa da plataforma

Skills de Conhecimento (.md):

  • Retornam conteúdo markdown estático quando chamadas
  • Usam frontmatter YAML com campos name e description
  • Ideais para FAQs, políticas e informações de referência

Notas:

  • Skills são referenciadas em agentes IA como app:nome_do_app:nome_da_skill
  • Veja Skills de Agentes IA para documentação completa sobre como escrever skills

Sem o arquivo manifest.json, o upload do seu app falhará com um erro :manifest_not_found.

Estrutura do Arquivo ZIP

Seu app deve seguir esta estrutura:

my-app.zip
├── my-app.lua # Arquivo principal (obrigatório, deve corresponder ao nome do app)
├── my-app/ # Opcional: Diretório de módulos
│ ├── utils.lua
│ ├── handlers.lua
│ └── api/
│ └── client.lua
└── assets/ # Obrigatório: Contém manifest e arquivos estáticos
├── manifest.json # OBRIGATÓRIO: Metadados do app
├── liquid/ # Templates Liquid (usados por turn.liquid.render)
│ └── welcome.liquid
├── public/ # Arquivos estáticos servidos via HTTP
│ ├── svg/
│ │ └── icon.svg
│ ├── css/
│ │ └── styles.css
│ └── images/
│ └── logo.png
├── templates/
│ └── welcome.md
└── images/
└── logo.png

Importante: O diretório assets/ agora é obrigatório (não opcional) já que deve conter o arquivo manifest.json.

assets/public/ — Arquivos Estáticos Servidos via HTTP

Arquivos em assets/public/ são servidos diretamente via HTTP em /apps/:uuid/static/*path, sem passar pelo runtime Lua. Use isso para imagens, SVGs, folhas de estilo, fontes e quaisquer outros arquivos que precisam ser carregados por um navegador.

  • Em templates Liquid, referencie-os com o filtro static_url: {{ "svg/icon.svg" | static_url }}
  • Em código Lua, use turn.assets.static_url("svg/icon.svg") para gerar a URL

Apenas arquivos dentro de assets/public/ são acessíveis publicamente. Outros diretórios em assets/ (como liquid/, templates/, etc.) não são servidos via HTTP.

Usando o Docker SDK (Recomendado)

Se você usou o Docker SDK para criar o scaffold do seu app, o empacotamento é simples:

make build

# Isso cria my_app-0.0.1.zip (versão do manifest.json)

O comando build automaticamente:

  • Inclui seu arquivo .lua principal
  • Inclui o diretório assets/ com manifest.json
  • Inclui quaisquer módulos no diretório lib/
  • Exclui arquivos de teste e arquivos de desenvolvimento

Usando o Script Zip-It

O template tradicional inclui um script zip-it.sh que automatiza o empacotamento:

./zip-it.sh

# O script irá remover quaisquer arquivos zip antigos,
# criar um novo arquivo zip com seu código mais recente

Empacotamento Manual

Se você precisar de mais controle sobre o empacotamento:

# App simples (arquivo único)
zip payment-processor-1.0.0.zip payment-processor.lua

# App com módulos
zip -r my-app-1.0.0.zip my-app.lua my-app/

# App com ativos
zip -r my-app-1.0.0.zip my-app.lua my-app/ assets/

# Excluir arquivos de teste
zip -r my-app-1.0.0.zip my-app.lua my-app/ assets/ -x "*/spec/*" "*/turn.lua" "*.rockspec"

# Verificar conteúdo
unzip -l my-app-1.0.0.zip

Notas Importantes

NÃO inclua no seu ZIP:

  • Arquivos de teste (diretório spec/)
  • Mock da API Turn (turn.lua)
  • Arquivos de desenvolvimento (*.rockspec, .git/, etc.)
  • Arquivos de documentação (README.md, a menos que em assets/)

INCLUA:

  • Arquivo principal do app (deve corresponder ao nome do app)
  • Arquivo assets/manifest.json (OBRIGATÓRIO)
  • Todos os módulos Lua necessários
  • Pasta de ativos com manifest.json e quaisquer templates/imagens
  • Quaisquer arquivos de configuração que seu app precise

Processo de Upload

  1. Vá para a plataforma Turn.io
  2. Navegue até Configurações → Apps
  3. Clique em "Carregar Novo App"
  4. Selecione seu arquivo ZIP
  5. A plataforma validará:
    • Presença de assets/manifest.json
    • Estrutura JSON válida no manifest
    • Campos obrigatórios (name, version, description)
    • Formato de versionamento semântico
  6. Após validação bem-sucedida, uma definição de app será criada

Implantação Remota com o SDK

Em vez de construir arquivos ZIP manualmente e enviá-los pela UI, você pode enviar os arquivos do app diretamente para uma instância Turn em execução usando os comandos de implantação remota do SDK. Este é o fluxo de trabalho recomendado para desenvolvimento ativo.

Pré-requisitos

  • Seu app foi criado com o Docker SDK (turn-app new)
  • O app já está instalado na instância Turn de destino (a instalação inicial é feita pela UI)
  • Você possui um token de autenticação da API para a instância de destino

Configurando Targets

Um target é uma referência nomeada para uma instância Turn. Configure targets no diretório do seu app:

# Adicionar um target
make target-add NAME=staging URL=https://sua-instancia.turn.io

# Listar targets configurados
make target-list

# Remover um target
make target-remove NAME=staging

A configuração de targets é armazenada localmente em .turn-targets.json no diretório do seu app (adicione ao .gitignore).

Autenticação

Defina a variável de ambiente TURN_APP_TOKEN com seu token de API antes de executar comandos de push:

export TURN_APP_TOKEN=seu-token-de-api

Você também pode passar inline com cada comando:

TURN_APP_TOKEN=seu-token make push TARGET=staging

Enviando Arquivos

Envie os arquivos do seu app para um target remoto:

# Enviar todos os arquivos
make push TARGET=staging

# Enviar apenas arquivos alterados (mais rápido para atualizações incrementais)
make push TARGET=staging CHANGED=--changed

Quando você faz push, o SDK:

  1. Lê a versão atual do assets/manifest.json
  2. Calcula a próxima versão de desenvolvimento (ex: 1.0.01.0.1-dev.11.0.1-dev.2)
  3. Faz upload dos arquivos para a instância remota
  4. Atualiza o assets/manifest.json local com a nova versão

A instância remota recarrega automaticamente o app com os novos arquivos.

Modo Watch (Push Automático)

Para um ciclo de desenvolvimento ao vivo, use o modo watch para enviar alterações automaticamente conforme você edita arquivos:

make push-watch TARGET=staging

Isso monitora o diretório do seu app por alterações de arquivos e os envia para o target a cada salvamento — similar ao make watch para testes, mas enviando para uma instância remota.

Promovendo para Release

Os pushes de desenvolvimento criam versões de pré-lançamento (ex: 1.0.1-dev.3). Quando estiver pronto para lançar, promova para uma versão estável:

# Promover com incremento de patch (1.0.1-dev.3 → 1.0.1)
make promote TARGET=staging

# Promover com incremento minor (1.0.1-dev.3 → 1.1.0)
make promote TARGET=staging BUMP=--minor

# Promover com incremento major (1.0.1-dev.3 → 2.0.0)
make promote TARGET=staging BUMP=--major

A promoção envia todos os arquivos (não apenas os alterados) para garantir que a versão de release seja um snapshot completo do seu app.

Gerenciamento de Versões

O SDK gerencia versões automaticamente através do seu assets/manifest.json:

  • Pushes de desenvolvimento incrementam o número de pré-lançamento dev: 1.0.01.0.1-dev.11.0.1-dev.2
  • Promote remove a tag de pré-lançamento e aplica o incremento: 1.0.1-dev.51.0.1 (patch), 1.1.0 (minor), ou 2.0.0 (major)

Você pode inspecionar todas as versões implantadas em um target:

make versions TARGET=staging

Rollback

Se precisar reverter para uma versão anterior:

make rollback TARGET=staging VERSION=1.0.0

Puxando Jornadas da Produção

Jornadas são frequentemente editadas em produção via a interface do canvas. Para sincronizar essas alterações de volta ao seu repositório local:

make pull TARGET=staging

Isso baixa o arquivo atual do app do servidor e sobrescreve seus arquivos locais em assets/journeys/. Seu diretório git deve estar limpo antes de puxar.

Após puxar, revise as alterações com suas ferramentas preferidas (git diff, seu editor, etc.) e faça commit seletivamente.

Proteção Contra Conflitos de Versão

O servidor rejeita pushes com uma versão mais antiga que a versão ativa atual (HTTP 409). Isso pode acontecer se seu assets/manifest.json local estiver desatualizado — por exemplo, se outro desenvolvedor enviou uma versão mais recente.

Quando isso acontecer:

  1. Execute make versions TARGET=staging para ver as versões implantadas
  2. Atualize a versão no seu assets/manifest.json local para corresponder ou superar a versão remota atual
  3. Tente o push novamente

Fluxo de Trabalho Típico de Desenvolvimento

1. Criar app:              turn-app new my_app
2. Desenvolver e testar: make watch (execução automática de testes)
3. Configurar target: make target-add NAME=staging URL=https://...
4. Instalar via UI: Fazer upload do ZIP inicial em Configurações → Apps
5. Enviar para remoto: make push-watch TARGET=staging (ciclo de dev ao vivo)
6. Lançar: make promote TARGET=staging
7. Puxar alterações: make pull TARGET=staging (sincronizar edições de jornadas)
8. Iterar: Continue enviando versões dev, promova quando pronto

Fornecendo Documentação do App na UI

Para garantir uma ótima experiência do usuário, você pode fornecer documentação que aparecerá diretamente na UI da Turn.io. Lide com o evento get_app_info_markdown e retorne uma string Markdown.

Este é o lugar perfeito para explicar o que seu app faz, listar suas funcionalidades e fornecer instruções de configuração ou exemplos de endpoint de API.

elseif event == "get_app_info_markdown" then
return [[
# Meu App Incrível

Este app se integra com o serviço externo `XYZ`.

## Configuração

Para usar este app, forneça sua `XYZ_API_KEY` na configuração abaixo.

## Endpoint de Webhook

Envie requisições `POST` do seu serviço para a seguinte URL:
`/apps/]] .. app.uuid .. [[/webhook`
]]
end