Pular para o conteúdo

Como um conector funciona

Esta parte é para quem quer entender, revisar ou depurar o que a IA escreve. Conectores são escritos pela IA de construção ou, por quem tem TI própria, no próprio editor com a CLI.

Um conector é:

  • um arquivo TypeScript que exporta como default uma função que recebe o ctx;
  • um manifesto JSON que declara o cron, os hosts, as rotas, os agentes, os segredos e se o conector escreve fora.

O código é compilado num arquivo só. Imports só do Node ou de tipos; não há pacotes de terceiros, rede, sistema de arquivos útil nem variáveis de ambiente. Tudo o que sai do código passa pelo ctx.

Uma execução roda em fatias. Em cada fatia a função é chamada de novo, do começo, com o mesmo ctx.state. O padrão é sempre o mesmo:

  1. Ler o cursor de ctx.state (a data da última alteração lida, a última chave).
  2. Ler uma página da origem a partir do cursor.
  3. Processar e gravar o novo cursor com ctx.state.set.
  4. Se ctx.shouldYield() for true, devolver { done: false } para continuar na próxima fatia; quando não houver mais nada, devolver { done: true } (ou nada).

O ctx.state sobrevive entre execuções: a próxima execução começa de onde a última que terminou bem (sucesso ou parcial) parou. Uma execução que falha, ou uma execução real com escrita que ficou guardada esperando confirmação, não avança o cursor, então nada se perde. Zere no estado o que só serve a uma execução (o número da página, por exemplo).

Chamada Uso No ensaio Em produção
ctx.http.request Ler, com qualquer método (um POST que só consulta vai aqui). De verdade De verdade
ctx.http.write Criar, alterar ou apagar algo em outro sistema. Registrada, não sai (planned) Exige externalWrites; a primeira em cada host espera confirmação
client.read.<Op> (SOAP) Operação SOAP que só consulta. De verdade De verdade
client.write.<Op> (SOAP) Operação SOAP que muda algo. Registrada, não sai Como http.write
ctx.db.query Um SELECT num banco da rede da loja, pelo agente. De verdade De verdade

Uma escrita feita por ctx.http.request passaria no ensaio como leitura: por isso o ensaio lista todo POST, PUT, PATCH e DELETE feito por request (writesAsReads) para uma pessoa conferir. Toda escrita na Zoppy usa write.

Quando planned vem true, a escrita não chegou ao outro sistema (ensaio, ou a primeira escrita esperando confirmação). O código deve seguir normalmente, sem tratar como erro.

O gateway não deduplica: quem garante que nada se repete é o conector. Toda escrita, na Zoppy ou em outro sistema, é:

  • um upsert pela chave natural (id externo, documento, e-mail ou telefone), com PUT ou PATCH no id do registro; ou
  • uma criação (POST) feita depois de consultar, na mesma execução, se o registro já existe: um GET pela chave, ou a rota de leitura declarada em readRoutes, na mesma coleção.

Criar sem essa consulta é proibido, porque o cursor é relido: depois que uma escrita guardada é confirmada, depois de uma queda e no reprocesso da quarentena, o mesmo registro passa de novo pelo conector.

O ensaio confere a regra. Um POST que é escrita (fora de readRoutes) num caminho sem nenhuma leitura antes, na mesma execução, no mesmo host e na mesma coleção (o caminho sem os ids do fim: /customers/123 e /customers são a mesma coleção), volta como problema connect.write_without_lookup. Esse ensaio não conta como limpo e a publicação é recusada com 409 connect.write_without_lookup até um ensaio sem ele. PUT e PATCH contam como upsert e não são cobrados.

Um registro que o outro lado recusa com 4xx, ou que não dá para mapear, vai para ctx.quarantine(registro, motivo) e o conector segue com o próximo. Falha geral (o sistema fora do ar, credencial errada) é erro: deixe a exceção subir para a execução falhar e a próxima retomar do cursor.

No reprocesso, a execução recebe os registros em ctx.input.quarantined ([{ id, record, reason }]) em vez de ler a origem; o que ela não mandar de volta para a quarentena sai como resolvido.

ctx.count(objeto, tipo, quantidade) alimenta o resumo da execução: read, created, updated, skipped e failed, por objeto (clientes, pedidos, produtos). Uma contagem failed deixa a execução partial.

  • Uma consulta por chamada, só SELECT (ou WITH ... SELECT), recusada no gateway e de novo no agente se mudar algo.
  • Paginação pela chave: ORDER BY chave e WHERE chave > :ultima, com a última chave em ctx.state, e maxRows até o limite do gateway. truncated: true quer dizer que há mais linhas.
  • Marcadores de parâmetro de cada motor: SQL Server @p1, Oracle :1, Firebird e MySQL ?, Postgres $1.
  • Decimais chegam como texto exato e datas como texto ISO.
  • No reconhecimento, a IA descobre tabelas e colunas pelas visões de catálogo de cada banco e olha os dados só em agregado.

Declare o nome em secrets no manifesto e use ctx.secret('NOME') onde o valor deve ir (um cabeçalho, um parâmetro, o soapHeader). A chamada devolve uma referência que o gateway troca pelo valor na saída: o valor nunca está no código, nos logs nem na conversa.

Uma recusa do gateway lança ConnectGatewayError com status e code (connect.route_not_allowed, connect.agent_offline…). Um limite da execução lança ConnectLimitError; mesmo capturado, a execução termina com falha por aquele motivo. Veja o catálogo de erros e os limites.