Menu
4/9 Protocolo do sidecarTodas as páginas

Tutoriais · Plugins

Protocolo do sidecar

Como um sidecar conversa com o Jarvis ADE: variáveis de ambiente, mensagens de entrada e saída em JSON lines, ciclo de vida, restart e backoff.

Nesta página

Visão geral

Um sidecar é um processo filho que o Jarvis ADE inicia com o comando do manifesto, com a pasta instalada do plugin como cwd. A conversa é em JSON lines: um objeto JSON por linha, sem quebras de linha dentro dele.

  • stdin (app → sidecar): hello, eventos de missão e do quadro, e shutdown.
  • stdout (sidecar → app): ready, log, card.note, card.link, resync, error.
  • stderr: cada linha vira um log de nível warn.

O sidecar só é iniciado quando o plugin está ativado e todas as configurações com required: true têm valor. Uma falha do sidecar (linha ilegível, crash, timeout) é registrada no log da integração e nunca derruba o app.

Variáveis de ambiente

O processo herda o process.env do app, mais o env declarado na integração, mais as variáveis abaixo (as ADE_* vencem em caso de conflito):

Variáveis de ambiente do sidecar
VariávelValor
ADE_PLUGIN_IDo id do plugin.
ADE_PLUGIN_INTEGRATION_IDo id da integração.
ADE_PLUGIN_DATA_DIRcaminho absoluto userData/plugin-data/<plugin>/<integração>. O app cria a pasta antes de iniciar; é o lugar para estado, cache e temporários.
ADE_PLUGIN_SETTINGSJSON plano de strings: os default do manifesto com os valores que o usuário preencheu por cima. Faça JSON.parse com try/catch.
ADE_PROTOCOL_VERSIONsempre "1" hoje.
ADE_LOCALEidioma do app (ex.: pt-BR).

Mensagens do app para o sidecar (stdin)

Toda linha tem o envelope { v: 1, id, at, type, ...payload }: v é a versão do protocolo, id um UUID por mensagem e at o Date.now() da emissão.

hello (sempre a primeira linha)
{"v":1,"id":"6f1c…","at":1789744976234,"type":"hello","adeVersion":"0.1.0","pluginId":"hello-ade","integrationId":"echo","settings":{"greeting":"hello"}}
board.card_moved (exemplo abreviado)
{
  "v": 1,
  "id": "0b8d…",
  "at": 1789745131936,
  "type": "board.card_moved",
  "mission": { "id": "…", "name": "…", "workspaceId": "…", "workspaceName": "…", "status": "running", "createdAt": 1789744899624 },
  "columns": [{ "id": "…", "title": "Backlog", "order": 0, "role": "backlog" }],
  "card": { "id": "…", "title": "…", "columnId": "…", "assignee": null, "columnTitle": "Em desenvolvimento", "columnRole": "dev" },
  "from": { "id": "…", "title": "Backlog", "order": 0, "role": "backlog" },
  "to": { "id": "…", "title": "Em desenvolvimento", "order": 1, "role": "dev" }
}
Tipos de mensagem enviados ao sidecar
typeQuandoCampos além do envelope
helloLogo após cada início do processoadeVersion, pluginId, integrationId, settings
board.snapshotDepois do hello, um por missão com quadro ativo; e de novo por resyncmission, columns, cards
mission.createdMissão criadamission
mission.renamedMissão renomeadamission
mission.finishedMissão encerradamission, status, summary (pode ser null)
board.columns_setColunas definidas/reordenadasmission, columns
board.card_createdCard criadomission, columns, card
board.card_updatedCard atualizadomission, columns, card, changed (lista de title | description | docs | assignee | parent), note (nota de atividade dessa atualização, ou null)
board.card_movedCard movido de colunamission, columns, card, from, to
board.card_deletedCards removidosmission, cardIds
board.card_attachedEvidência anexadamission, columns, card, attachment (path absoluto, legível na mesma máquina)
board.card_question_asked / _answeredPergunta feita / respondidamission, columns, card, question
shutdownO app vai encerrar o sidecarnenhum

Formas que se repetem

  • mission: id, name, workspaceId, workspaceName, status (draft | planning | running | paused | merging | done | aborted | failed), createdAt (ms).
  • coluna: id, title, order, role (backlog | dev | qa_wait | qa_test | bug | done ou null).
  • card: o card completo (id, missionId, columnId, parentId, type story | task | subtask, title, description, docs, activity, attachments, questions, order, createdAt, updatedAt…) mais assignee ({ paneId, label, agentId } ou null), columnTitle e columnRole.

Mensagens do sidecar para o app (stdout)

exemplos (uma por linha)
{"type":"ready","name":"meu-plugin"}
{"type":"log","level":"info","message":"sincronizado"}
{"type":"card.note","cardId":"<id do card>","message":"✅ espelhado no serviço"}
{"type":"card.link","cardId":"<id do card>","url":"https://exemplo.com/t/123","label":"Tarefa #123"}
{"type":"resync"}
{"type":"error","message":"falha ao autenticar"}
Mensagens que o sidecar envia ao app
typeCamposEfeito
readyname?Estado running. Envie assim que terminar de inicializar.
loglevel (info | warn | error), messageEntra no painel de log da integração. Nível desconhecido vira info.
card.notecardId, messageNota na atividade do card, com autor plugin:<id do plugin>, sem checagem de atribuição.
card.linkcardId, url, labelAtalho para uma nota 🔗 <label>: <url>.
resyncmissionId?Reenvia board.snapshot da missão (ou de todas, sem missionId).
errormessageEntra no log como error; o processo continua rodando.
  • Uma linha do stdout que não é JSON (ou é JSON sem type textual, ou com um type desconhecido) vira um log info com o texto da linha — útil para depurar, mas prefira mensagens log explícitas.
  • Cada mensagem de log é cortada em 500 caracteres; o painel guarda as últimas 50 entradas.
  • Um card.note para um card que não existe mais gera um aviso no log — não derruba nada.

Ciclo de vida, restart e backoff

Estados de uma integração
EstadoSignificado
disabledO plugin está desativado.
missing_settingsFalta valor em uma configuração required; o processo não é iniciado.
startingDo início do processo até o ready — ou até 10 s.
runningRecebendo eventos.
stoppedParado de propósito (restart, desativação, remoção, fim do app).
errorDesistiu: mais de 5 saídas inesperadas em 10 minutos, ou falha ao iniciar.
Tempos e limites
O quêValor
Espera pelo ready10 s. Sem ready, o estado vira running mesmo assim, com um aviso no log.
Encerramento graciosoLinha shutdown → stdin fechado → 5 s de espera → SIGTERM.
Backoff após um crash1 s, 2 s, 4 s, 8 s, 16 s: o atraso depende de quantas saídas inesperadas houve nos últimos 10 minutos (1ª → 1 s … 5ª → 16 s).
DesistênciaMais de 5 saídas inesperadas em 10 minutos → error. O botão Reiniciar zera o contador.

O que dispara um restart gracioso

  • Salvar as configurações do plugin.
  • Ativar o plugin, ou reimportá-lo.
  • O botão Reiniciar na tela do plugin.

Em todo início do processo — restart gracioso, reinício depois de um crash com backoff, abertura do app — o sidecar recebe de novo o hello (com as configurações atuais) e os board.snapshot. Trate o hello como “começando do zero”, nunca como “o usuário acabou de fazer algo”. É exatamente por isso que ações disparadas por configuração precisam de uma trava anti-repetição.

Filtro de eventos

Com events no manifesto, só chegam os eventos de board.*/mission.* que combinam com um nome exato ou um prefixo algo.*. O filtro também vale para o board.snapshot. Sem filtro, ou com [], chega tudo.

Checklist de um sidecar correto

  • Ler o stdin linha a linha (node:readline) e fazer JSON.parse dentro de um try/catch.
  • Enviar { type: 'ready' } logo que estiver pronto.
  • Ao receber shutdown (ou quando o stdin fechar), limpar e sair com process.exit(0) em menos de 5 s.
  • Só mensagens do protocolo no stdout — uma por linha. Tudo que for diagnóstico solto vai para o stderr ou para uma mensagem log.
  • Nunca deixar uma exceção sem tratamento nem bloquear esperando I/O sem timeout.