sapiens-mcp 1.86.1 → 1.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/registry.js +9 -2
- package/dist/skills.js +32 -0
- package/dist/tools/distribution.js +41 -5
- package/dist/tools/meta.js +3 -25
- package/dist/tools/pipeline.js +5 -3
- package/dist/tools/publicar.js +102 -0
- package/dist/tools/video.js +3 -2
- package/package.json +1 -1
package/dist/registry.js
CHANGED
|
@@ -38,6 +38,7 @@ import { publicidade, publicidadeSchema } from "./tools/publicidade.js";
|
|
|
38
38
|
import { reference, referenceSchema } from "./tools/reference.js";
|
|
39
39
|
import { trilhas, trilhasSchema } from "./tools/trilhas.js";
|
|
40
40
|
import { semana, semanaSchema } from "./tools/semana.js";
|
|
41
|
+
import { publicar, publicarSchema } from "./tools/publicar.js";
|
|
41
42
|
import { distribution, distributionSchema } from "./tools/distribution.js";
|
|
42
43
|
import { shareDrop, shareDropSchema } from "./tools/shareDrop.js";
|
|
43
44
|
import { skill, skillSchema } from "./tools/skill.js";
|
|
@@ -224,10 +225,15 @@ export const TOOLS = {
|
|
|
224
225
|
handler: trilhas,
|
|
225
226
|
},
|
|
226
227
|
sapiens_distribution: {
|
|
227
|
-
description: "Despachar peças (Distribution Workflow) — a peça pronta entra na FILA com canal, copy e a aba de quem ela é. ADMIN-ONLY: despacho depende de rede conectada e hoje só a casa tem. NADA aqui posta em rede nenhuma; quem despacha é a tela em /experimentos/distribution-workflow, que é onde a conta conectada mora. A ABA (`voice`) responde de quem é a peça: sapiens (a casa), helen-ailith, borderless, ou o slug de um personagem seu — slug livre, então personagem novo ganha aba sozinho na primeira peça dele, sem deploy. Em queue, aba ausente = o servidor deriva do personagem da peça e cai em sapiens quando não há. Sub-actions: queue (põe na fila: title e channel obrigatórios, mais copy/hashtags/assetUrl/notes; replyText é o comentário que sai junto do post, onde vai o link, e só o X (resposta no fio) e a Página do LinkedIn (primeiro comentário) publicam, o resto recusa; num canal do Instagram, format instagram_story faz a peça sair como STORY: uma mídia só, sem legenda nem link, some em 24h). Em queue, DOIS campos que parecem opcionais e não são: `lang` diz em que língua a copy já está, e sem ele canal de rede internacional reescreve a sua copy sozinho, dois segundos depois, na voz da casa; `assetPageAssetId` é o imageId da obra da casa, e sem ele a peça só encontra a obra se a assetUrl bater letra a letra, então versão web de um master nasce órfã de ficha, list (o que está na fila, filtrável por status e por aba), voices (as abas que existem hoje com a contagem de cada uma), status (move a peça de lane: fila, agendado, postado, descartado; postado aceita postUrl e fecha o rastro, agendado aceita scheduledFor em ms), sign (liga ou desliga a ASSINATURA da peça, imagem e vídeo do CDN da casa, com o carimbo da LINHAGEM de quem assina: sint.fyi/<endereço dela> no canto e o fecho de 2s no vídeo, ou a marca própria dela sem nada da casa; itemId, e signed com true de default; o carimbo da casa é recusado em peça +18 e praça adulta, a marca própria passa; o carimbo leva uns 30s e até lá a peça não sai; em queue, signed:true já enfileira assinada), bio (lê ou troca a BIO de uma conta da casa no Bluesky, channel bluesky ou bluesky-2 a bluesky-5: sem texto só lê; bioLine acrescenta uma linha no fim sem mexer no resto e não repete linha que já está lá; bio reescreve a inteira; teto de 256; ESTA sub-action muda o perfil público na hora, e bio não aceita hiperlink, o endereço aparece escrito), lineages (o CADASTRO das linhagens, o mesmo da aba Teia: de cada personagem, produto e da casa, a mãe no Bluesky onde a peça nasce, as contas próprias, as galerias que a mãe alimenta sozinha, as fichas e o post do dia, mais as regras que a teia desdobra e as vagas de conta ainda sem dona), lineage-new (nasce uma linhagem: nome obrigatório, grupo, artigo, lang; nasce sem mãe, então nada viaja sozinho até o lineage-save dar a mãe), lineage-save (slug e só o que muda: mae, contas rede->canal, galerias, fichaIds, rotina, contaNaCasa, assinatura {estilo casa|propria|nenhuma, marca, padrao}; o servidor recusa conta que já é de outra linhagem e canal de outra rede, e a vaga assumida ganha o nome da linhagem), lineage-archive (slug + arquivada), tree (a ÁRVORE DE CONTAS, o formulário de quem existe onde: linhagem × rede, com o email, o @, o canal e o estado de cada conta, ligada, desligada, fora, falta ou dispensa, mais o passo pra ligar cada rede; slug filtra uma linhagem), tree-mark (slug + celulas [{rede, handle, canal, dispensada}]: registra o email ou o @ que existe lá fora, associa a conta da casa ou dispensa rede opcional, a linhagem inteira de uma vez e tudo ou nada; X, Instagram, Bluesky, email e Telegram são obrigatórias e não se dispensam), garimpo (o ACERVO em famílias pra escolher o que vai pra fila: só as candidatas saem, a versão velha de um corte ou upscale vem marcada como substituída, as irmãs da mesma ideia vêm agrupadas pra escolha no olho, o painel cortado vem em recortes (o original e os quadros em ordem, que só saem juntos em carrossel), e cada família diz por onde já saiu; paraCanal tira o que já foi pro canal pedido, soResumo conta sem listar, página por cursor em antesDe; o passo a passo é a skill distribuir). Sem custo em Sinapses: isto organiza, não gera. A copy segue a voz de QUEM ASSINA a peça, que não é sempre a voz da casa: peça da Helen fala como a Helen. E a peça sai pela CONTA de quem assina ela: conta extra da mesma rede é um canal próprio (bluesky-2 é outra conta no Bluesky, instagram-pro outra no Instagram), então case o canal com a aba antes de despachar.",
|
|
228
|
+
description: "Despachar peças (Distribution Workflow) — a peça pronta entra na FILA com canal, copy e a aba de quem ela é. ADMIN-ONLY: despacho depende de rede conectada e hoje só a casa tem. NADA aqui posta em rede nenhuma; quem despacha é a tela em /experimentos/distribution-workflow, que é onde a conta conectada mora. A ABA (`voice`) responde de quem é a peça: sapiens (a casa), helen-ailith, borderless, ou o slug de um personagem seu — slug livre, então personagem novo ganha aba sozinho na primeira peça dele, sem deploy. Em queue, aba ausente = o servidor deriva do personagem da peça e cai em sapiens quando não há. Sub-actions: queue (põe na fila: title e channel obrigatórios, mais copy/hashtags/assetUrl/notes; replyText é o comentário que sai junto do post, onde vai o link, e só o X (resposta no fio) e a Página do LinkedIn (primeiro comentário) publicam, o resto recusa; num canal do Instagram, format instagram_story faz a peça sair como STORY: uma mídia só, sem legenda nem link, some em 24h). Em queue, DOIS campos que parecem opcionais e não são: `lang` diz em que língua a copy já está, e sem ele canal de rede internacional reescreve a sua copy sozinho, dois segundos depois, na voz da casa; `assetPageAssetId` é o imageId da obra da casa, e sem ele a peça só encontra a obra se a assetUrl bater letra a letra, então versão web de um master nasce órfã de ficha, list (o que está na fila, filtrável por status e por aba, com copy, hashtags e notas de cada peça; fila maior que o limit se lê inteira pelo cursor antesDe=proximoCursor), voices (as abas que existem hoje com a contagem de cada uma), status (move a peça de lane: fila, agendado, postado, descartado; postado aceita postUrl e fecha o rastro, agendado aceita scheduledFor em ms), edit (reescreve a peça da FILA sem tirar ela do lugar: itemId e só o que muda, copy, title, publicTitle, hashtags, notes, lang, replyText, assetUrl/assetKind ou assets; é o gesto pra corrigir legenda, nunca descartar e enfileirar de novo; texto de peça postada o servidor recusa), sign (liga ou desliga a ASSINATURA da peça, imagem e vídeo do CDN da casa, com o carimbo da LINHAGEM de quem assina: sint.fyi/<endereço dela> no canto e o fecho de 2s no vídeo, ou a marca própria dela sem nada da casa; itemId, e signed com true de default; o carimbo da casa é recusado em peça +18 e praça adulta, a marca própria passa; o carimbo leva uns 30s e até lá a peça não sai; em queue, signed:true já enfileira assinada), bio (lê ou troca a BIO de uma conta da casa no Bluesky, channel bluesky ou bluesky-2 a bluesky-5: sem texto só lê; bioLine acrescenta uma linha no fim sem mexer no resto e não repete linha que já está lá; bio reescreve a inteira; teto de 256; ESTA sub-action muda o perfil público na hora, e bio não aceita hiperlink, o endereço aparece escrito), lineages (o CADASTRO das linhagens, o mesmo da aba Teia: de cada personagem, produto e da casa, a mãe no Bluesky onde a peça nasce, as contas próprias, as galerias que a mãe alimenta sozinha, as fichas e o post do dia, mais as regras que a teia desdobra e as vagas de conta ainda sem dona), lineage-new (nasce uma linhagem: nome obrigatório, grupo, artigo, lang; nasce sem mãe, então nada viaja sozinho até o lineage-save dar a mãe), lineage-save (slug e só o que muda: mae, contas rede->canal, galerias, fichaIds, rotina, contaNaCasa, assinatura {estilo casa|propria|nenhuma, marca, padrao}; o servidor recusa conta que já é de outra linhagem e canal de outra rede, e a vaga assumida ganha o nome da linhagem), lineage-archive (slug + arquivada), tree (a ÁRVORE DE CONTAS, o formulário de quem existe onde: linhagem × rede, com o email, o @, o canal e o estado de cada conta, ligada, desligada, fora, falta ou dispensa, mais o passo pra ligar cada rede; slug filtra uma linhagem), tree-mark (slug + celulas [{rede, handle, canal, dispensada}]: registra o email ou o @ que existe lá fora, associa a conta da casa ou dispensa rede opcional, a linhagem inteira de uma vez e tudo ou nada; X, Instagram, Bluesky, email e Telegram são obrigatórias e não se dispensam), garimpo (o ACERVO em famílias pra escolher o que vai pra fila: só as candidatas saem, a versão velha de um corte ou upscale vem marcada como substituída, as irmãs da mesma ideia vêm agrupadas pra escolha no olho, o painel cortado vem em recortes (o original e os quadros em ordem, que só saem juntos em carrossel), e cada família diz por onde já saiu; paraCanal tira o que já foi pro canal pedido, soResumo conta sem listar, página por cursor em antesDe; o passo a passo é a skill distribuir). Sem custo em Sinapses: isto organiza, não gera. A copy segue a voz de QUEM ASSINA a peça, que não é sempre a voz da casa: peça da Helen fala como a Helen. E a peça sai pela CONTA de quem assina ela: conta extra da mesma rede é um canal próprio (bluesky-2 é outra conta no Bluesky, instagram-pro outra no Instagram), então case o canal com a aba antes de despachar.",
|
|
228
229
|
schema: distributionSchema,
|
|
229
230
|
handler: distribution,
|
|
230
231
|
},
|
|
232
|
+
sapiens_publicar: {
|
|
233
|
+
description: "Publicar uma peça SUA nas SUAS redes (TikTok, Instagram, YouTube, LinkedIn, Facebook, X, Threads, Pinterest, Bluesky), pelo Distribution da pessoa (assinatura ou convite). Identidade SEMPRE pelo sessionToken. Sub-actions: 'painel' (as redes com conectada ou não, o @, o limite de texto e a régua de cada praça; com handle, a peça: onde ela já está por rede, com estado e link, e a sugestão de onde mandar, as que ficam de fora com o motivo e as que valeria conectar), 'rascunho' (handle + rede + legenda: guarda a legenda de UMA rede pra peça, vazia apaga; acima do limite da rede volta erro com o número), 'publicar' (handle + redes, agora ou agendarPara ISO com fuso; a legenda vai em BLOCOS: text é o corpo, cta a chamada, hashtags sem #, e cada rede leva o que cabe no limite dela, saindo primeiro as hashtags, depois a chamada, e o corpo corta em fim de frase; legendas {rede: texto} passa por cima dos blocos naquela rede; o retorno diz em `adaptadas` quais redes receberam menos do que foi escrito), 'lista' (o que já saiu: os envios recentes com o estado de cada rede), 'conferir' (postId: pergunta o que cada rede fez e grava o link), 'cancelar' (postId: cancela agendado que ainda não saiu). Fluxo: painel com handle, escolhe pela sugestão, publicar, conferir. Só peça da própria pessoa, e só rede conectada (conectar é na tela /dashboard/distribution). Sem custo em Sinapses: o teto é o de publicações por ciclo do plano. Publicar POSTA DE VERDADE na conta da pessoa: confirme redes e legenda com ela antes. A fila da casa é outra tool (sapiens_distribution, admin). Passo a passo: skill publicar.",
|
|
234
|
+
schema: publicarSchema,
|
|
235
|
+
handler: publicar,
|
|
236
|
+
},
|
|
231
237
|
sapiens_semana: {
|
|
232
238
|
description: "As Atividades da SUA semana na casa: o que você produziu e quanto XP isso rendeu. Identidade SEMPRE pelo sessionToken. Sub-actions: 'get' (quais atividades você fez nesta semana, quantas peças em cada, o XP já pago, quanto a Casa Cheia renderia no fecho, quanto vale abrir mais uma atividade e QUAIS abrir agora, a sequência de semanas seguidas com o recorde, as semanas anteriores com o que cada uma rendeu, e uma leitura pronta da semana pra você dizer em voz alta), 'fechar' (fecha as semanas pendentes e credita a Casa Cheia de cada uma). Como a régua funciona: cada peça pronta anda o contador da atividade e paga em degraus (1a, 3a, 7a, 15a, 30a peça da semana, com retorno decrescente); no fecho, a LARGURA paga à parte, 2 atividades valem 20 XP, 5 valem 200, 8 valem 560, então atividades diferentes rendem mais que repetir a mesma. Esta tool PAGA XP, não cobra Sinapses. Idempotente: chamar 'fechar' duas vezes não credita duas vezes.",
|
|
233
239
|
schema: semanaSchema,
|
|
@@ -261,7 +267,7 @@ O passo a passo travado de cada fluxo mora na tool sapiens_skill, servida por es
|
|
|
261
267
|
- sapiens_skill action=list -> o índice (slug + quando usar cada uma).
|
|
262
268
|
- sapiens_skill action=get name=<slug> -> a skill inteira.
|
|
263
269
|
Slugs: ${skillMenuLine()}
|
|
264
|
-
Puxe a skill ANTES de: gerar música ou efeito sonoro (musica), escolher modelo de vídeo (video), gerar imagem (imagem), criar personagem ou prompt de Midjourney (personagem), criar na identidade do usuário (studio), postar no Fórum ou no chat (forum-comunidade), redigir qualquer texto publicável (voz-da-casa), incorporar o Sintético (companhia), missão de Desafio (trilhas), montar currículo (curriculo), guardar obra ou ferramenta no acervo (repertorio), gerar em motor de fora com crédito próprio e trazer a peça pro acervo (pontes). Perdido no começo: primeiros-passos.
|
|
270
|
+
Puxe a skill ANTES de: gerar música ou efeito sonoro (musica), escolher modelo de vídeo (video), gerar imagem (imagem), criar personagem ou prompt de Midjourney (personagem), criar na identidade do usuário (studio), postar no Fórum ou no chat (forum-comunidade), redigir qualquer texto publicável (voz-da-casa), incorporar o Sintético (companhia), missão de Desafio (trilhas), montar currículo (curriculo), guardar obra ou ferramenta no acervo (repertorio), gerar em motor de fora com crédito próprio e trazer a peça pro acervo (pontes), publicar peça nas redes da pessoa (publicar). Perdido no começo: primeiros-passos.
|
|
265
271
|
Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
|
|
266
272
|
|
|
267
273
|
REGRA DE OURO:
|
|
@@ -330,6 +336,7 @@ const TOOL_TITLES = {
|
|
|
330
336
|
sapiens_trilhas: "Trilhas & Desafios",
|
|
331
337
|
sapiens_distribution: "Despachar peças",
|
|
332
338
|
sapiens_semana: "Minha Semana",
|
|
339
|
+
sapiens_publicar: "Publicar nas minhas redes",
|
|
333
340
|
sapiens_instagram: "Auto-DM Instagram",
|
|
334
341
|
sapiens_share: "Share Drop",
|
|
335
342
|
sapiens_resume: "Currículo PRO",
|
package/dist/skills.js
CHANGED
|
@@ -273,6 +273,8 @@ Dá pra somar 1 vídeo de movimento via \`referenceVideoUrls\` (até 15s, host d
|
|
|
273
273
|
|
|
274
274
|
Frame inicial e final: \`startImageId\`/\`endImageId\` (galeria) ou \`startImageUrl\`/\`endImageUrl\`. Arquivo local (\`startImagePath\`) só funciona no MCP instalado via stdio, nunca no remoto. Suporte a frame final varia por modelo.
|
|
275
275
|
|
|
276
|
+
Frame de borda e referência são caminhos EXCLUSIVOS no Seedance 2.0/Fast/Mini/2.5 e no H3: ou o take nasce do frame (\`startImage*\`/\`endImage*\`), ou das referências (\`referenceImage*\`/\`referenceVideoUrls\`), nunca dos dois. No Seedance a mistura é recusada antes de cobrar; no H3 a referência troca o endpoint e o frame inicial fica de fora. Quer a pessoa da foto no take? Mande a foto como referência e descreva o primeiro quadro no prompt; quer o quadro exato? Só o frame inicial.
|
|
277
|
+
|
|
276
278
|
## É assíncrono
|
|
277
279
|
|
|
278
280
|
\`create\` cria o row, debita e volta NA HORA com \`{imageId, status:'rendering'}\`. Acompanhe com \`action=status imageId=<id>\` até 'completed' (traz a url) ou 'error'/'blocked'.
|
|
@@ -820,6 +822,36 @@ O que a peça vira: NATIVA, PRIVADA, publicável depois por \`action=publish\` (
|
|
|
820
822
|
- Motor exclusivo da casa: Helen TTS, Musicator, os templates de take (\`templateSlug\`), Sombras, o carimbo assinado. Isso só existe em Sinapse.
|
|
821
823
|
- A pessoa tem Sinapse e o take é curto: \`sapiens_video\` é um comando só, com a régua de qualidade da casa. Menos atrito vence quando o custo cabe.
|
|
822
824
|
- Peça achada pronta na internet, sem direção dela: é \`action=upload\` (privada pra sempre), não ingest.`,
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
name: "publicar",
|
|
828
|
+
title: "Publicar nas redes da pessoa",
|
|
829
|
+
description: "A peça da pessoa saindo pelas contas dela: ler o painel, escolher as redes pela sugestão, escrever a legenda em blocos, confirmar e conferir. Puxe ANTES de sapiens_publicar.",
|
|
830
|
+
body: `## O que é
|
|
831
|
+
|
|
832
|
+
A pessoa liga as redes dela (TikTok, Instagram, YouTube, LinkedIn, Facebook, X, Threads, Pinterest, Bluesky) na tela do Distribution, e daqui a peça dela sai por essas contas, com o nome dela. Só peça DELA, e só rede que ela já ligou. Conectar rede nova é sempre na tela: \`/dashboard/distribution\`.
|
|
833
|
+
|
|
834
|
+
## O fluxo
|
|
835
|
+
|
|
836
|
+
1. \`sapiens_publicar action=painel handle=<img_... ou vid_...>\`. Volta:
|
|
837
|
+
- \`redes\`: cada rede com \`conectada\`, o \`conta\` (@), o \`copyMax\` (limite de texto) e o \`fit\` (o que rende lá).
|
|
838
|
+
- \`peca.porRede\`: onde a peça já está, uma linha por rede, com \`status\` (rascunho, enviado, agendado, publicado, falhou, cancelado) e \`url\` quando saiu.
|
|
839
|
+
- \`sugestao\`: \`sugeridas\` (conectadas, na ordem do que rende, com o motivo), \`fora\` (as que não levam essa peça e por quê) e \`valeConectar\` (não ligadas onde a peça iria bem).
|
|
840
|
+
2. Proponha as redes pela \`sugestao\`, na voz da pessoa. Não repita rede onde a peça já saiu, a não ser que ela peça.
|
|
841
|
+
3. Escreva a legenda em BLOCOS: \`text\` é o corpo, \`cta\` a chamada, \`hashtags\` a lista sem #. Cada rede leva o que cabe no limite dela: onde aperta, saem primeiro as hashtags, depois a chamada, e o corpo corta em fim de frase. Rede que precisa de texto próprio (o Threads curto, o LinkedIn mais longo) vai em \`legendas: {threads: "..."}\`. Texto publicável segue a skill voz-da-casa só se a peça for da casa; peça da pessoa fala na voz DELA.
|
|
842
|
+
4. **Mostre redes, legenda e horário e espere o ok.** Publicar posta de verdade na conta dela.
|
|
843
|
+
5. \`action=publicar\` com \`handle\`, \`redes\`, os blocos e, se for agendar, \`agendarPara\` em ISO com fuso (mínimo 5 minutos à frente, até um ano). O retorno traz \`adaptadas\`: diga quais redes receberam menos do que foi escrito.
|
|
844
|
+
6. Depois de uns minutos, \`action=lista\` e \`action=conferir postId=...\` pra ver o que cada rede fez e pegar o link.
|
|
845
|
+
|
|
846
|
+
## Rascunho
|
|
847
|
+
|
|
848
|
+
\`action=rascunho handle rede legenda\` guarda a legenda de uma rede sem mandar. Ela aparece no painel da tela quando a pessoa abrir a peça, e o envio usa ela. Legenda vazia apaga. Acima do limite da rede volta erro com o número: corte você, não deixe pra rede.
|
|
849
|
+
|
|
850
|
+
## Limites
|
|
851
|
+
|
|
852
|
+
- Sem custo em Sinapses. O teto é o do plano do Distribution: publicações por ciclo, mais o limite diário de cada rede. O erro diz qual bateu.
|
|
853
|
+
- Imagem não vai pra TikTok nem YouTube (só vídeo). Áudio não sai por aqui.
|
|
854
|
+
- Agendado se cancela com \`action=cancelar postId=...\` até 2 minutos antes da hora.`,
|
|
823
855
|
},
|
|
824
856
|
{
|
|
825
857
|
name: "carrossel",
|
|
@@ -23,6 +23,9 @@ import { convexQuery, convexMutation, convexAction, describeConvexError, getSess
|
|
|
23
23
|
* - list: o que está na fila, filtrável por status e por aba.
|
|
24
24
|
* - voices: as abas que existem hoje, com a contagem de cada uma.
|
|
25
25
|
* - status: move a peça de lane (fila, agendado, postado, descartado).
|
|
26
|
+
* - edit: reescreve a peça que está na fila (copy, título, hashtags,
|
|
27
|
+
* notas, mídia, língua, comentário). Só manda o que muda. Texto de peça
|
|
28
|
+
* POSTADA não se reescreve: o post na rede não muda junto.
|
|
26
29
|
* - sign: liga ou desliga a ASSINATURA da peça (imagem e vídeo). O carimbo
|
|
27
30
|
* é o da LINHAGEM de quem assina: sint.fyi/<endereço dela> (e o fecho de 2s
|
|
28
31
|
* no vídeo) ou a marca própria dela, sem nada da casa. A mesma chave da
|
|
@@ -149,6 +152,7 @@ export const distributionSchema = z.object({
|
|
|
149
152
|
"list",
|
|
150
153
|
"voices",
|
|
151
154
|
"status",
|
|
155
|
+
"edit",
|
|
152
156
|
"sign",
|
|
153
157
|
"bio",
|
|
154
158
|
"lineages",
|
|
@@ -170,12 +174,12 @@ export const distributionSchema = z.object({
|
|
|
170
174
|
copy: z
|
|
171
175
|
.string()
|
|
172
176
|
.optional()
|
|
173
|
-
.describe("queue: a legenda pronta pra colar, na voz de quem assina a peça. Sem em-dash."),
|
|
177
|
+
.describe("queue/edit: a legenda pronta pra colar, na voz de quem assina a peça. Sem em-dash. Em edit, troca a legenda inteira da peça da fila."),
|
|
174
178
|
replyText: z
|
|
175
179
|
.string()
|
|
176
180
|
.optional()
|
|
177
181
|
.describe("queue: o COMENTÁRIO que sai junto do post, pra onde vai o link (a copy fica sem ele e fecha apontando pro comentário). Só dois lugares publicam: o X pelo Buffer (x-buffer, x-buffer-2..4) e o X da API própria (x), como resposta no mesmo fio, teto 280; e a Página do LinkedIn pelo Buffer (linkedin-page, linkedin-page-2), como primeiro comentário, teto 1250. Threads (threads, threads-pro, threads-3..5), LinkedIn Perfil (linkedin) e o resto NÃO publicam comentário: lá o link mora na própria copy. O servidor recusa a peça com replyText num canal que não publica, e recusa o comentário acima do teto com a contagem; nada é cortado calado."),
|
|
178
|
-
hashtags: z.array(z.string()).optional().describe("queue: sem o #, o servidor limpa."),
|
|
182
|
+
hashtags: z.array(z.string()).optional().describe("queue/edit: sem o #, o servidor limpa. Em edit, a lista troca a inteira (vazia tira todas)."),
|
|
179
183
|
publicTitle: z
|
|
180
184
|
.string()
|
|
181
185
|
.max(100)
|
|
@@ -235,7 +239,7 @@ export const distributionSchema = z.object({
|
|
|
235
239
|
.number()
|
|
236
240
|
.optional()
|
|
237
241
|
.describe("queue, só canais fanvue e fanvue-dm: preço em CENTAVOS de dólar (pay-per-view). 1200 = $12. Piso de 300 na API deles, e peça paga EXIGE mídia. Ausente ou 0 = de graça pra quem já tem acesso."),
|
|
238
|
-
notes: z.string().optional().describe("queue: recado pra você mesmo na hora de postar."),
|
|
242
|
+
notes: z.string().optional().describe("queue/edit: recado pra você mesmo na hora de postar."),
|
|
239
243
|
format: z
|
|
240
244
|
.enum(["instagram_story"])
|
|
241
245
|
.optional()
|
|
@@ -244,7 +248,7 @@ export const distributionSchema = z.object({
|
|
|
244
248
|
.boolean()
|
|
245
249
|
.optional()
|
|
246
250
|
.describe("sign: true liga (o default), false desliga. queue: true enfileira a peça já com a assinatura ligada. A ASSINATURA carimba a IMAGEM e o VÍDEO da peça que moram no CDN da casa (o que sapiens_image/sapiens_video/sapiens_gallery devolvem), com o carimbo da LINHAGEM de quem assina a peça (a aba dela, ver lineages): no estilo casa, sint.fyi/<conta da linhagem na casa, ou o dono> no canto e, no vídeo, o fecho de 2 segundos no fim (o vídeo fica 2s mais longo); no estilo propria, só a marca da linhagem (ex: Velourie), sem nada da casa e sem fecho; no estilo nenhuma, o servidor recusa. O carimbo da casa é RECUSADO em peça +18 e em praça com teto adulto (fanvue, fanvue-dm, pornhub, civitai-red), porque sint.fyi é endereço da casa; a marca própria passa. A imagem do feed do Instagram é carimbada já recortada pra 4:5 a 1.91:1. O carimbo leva uns 30s na primeira vez (na hora quando a mídia já foi assinada antes), e enquanto ele não fica pronto a peça NÃO sai: o botão de postar da tela devolve o motivo."),
|
|
247
|
-
itemId: z.string().optional().describe("status/sign: o id da peça (vem do list ou do queue)."),
|
|
251
|
+
itemId: z.string().optional().describe("status/sign/edit: o id da peça (vem do list ou do queue)."),
|
|
248
252
|
status: z
|
|
249
253
|
.enum(["fila", "agendado", "postado", "descartado"])
|
|
250
254
|
.optional()
|
|
@@ -341,7 +345,7 @@ export const distributionSchema = z.object({
|
|
|
341
345
|
antesDe: z
|
|
342
346
|
.number()
|
|
343
347
|
.optional()
|
|
344
|
-
.describe("garimpo: o cursor, o `proximoCursor` da página anterior. Ausente = a página mais recente
|
|
348
|
+
.describe("garimpo e list: o cursor, o `proximoCursor` da página anterior. Ausente = a página mais recente. Em list, a fila com mais peças que o limit se lê inteira chamando de novo até proximoCursor voltar null."),
|
|
345
349
|
limite: z
|
|
346
350
|
.number()
|
|
347
351
|
.int()
|
|
@@ -469,7 +473,39 @@ export async function distribution(args) {
|
|
|
469
473
|
status: args.status,
|
|
470
474
|
voice: args.voice,
|
|
471
475
|
limit: args.limit,
|
|
476
|
+
antesDe: args.antesDe,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
if (args.action === "edit") {
|
|
480
|
+
if (!args.itemId)
|
|
481
|
+
throw new Error("action=edit exige itemId (vem do list ou do queue).");
|
|
482
|
+
const campos = {
|
|
483
|
+
title: args.title,
|
|
484
|
+
publicTitle: args.publicTitle,
|
|
485
|
+
copy: args.copy,
|
|
486
|
+
replyText: args.replyText,
|
|
487
|
+
hashtags: args.hashtags,
|
|
488
|
+
notes: args.notes,
|
|
489
|
+
lang: args.lang,
|
|
490
|
+
assetUrl: args.assetUrl,
|
|
491
|
+
assetKind: args.assetKind,
|
|
492
|
+
assets: args.assets,
|
|
493
|
+
};
|
|
494
|
+
const mudou = Object.entries(campos).filter(([, v]) => v !== undefined);
|
|
495
|
+
if (mudou.length === 0) {
|
|
496
|
+
throw new Error("action=edit: mande ao menos um campo pra trocar (copy, title, publicTitle, hashtags, notes, lang, replyText, assetUrl/assetKind ou assets).");
|
|
497
|
+
}
|
|
498
|
+
await convexMutation("distributionMcp:mcpUpdateItem", {
|
|
499
|
+
sessionToken,
|
|
500
|
+
itemId: args.itemId,
|
|
501
|
+
...Object.fromEntries(mudou),
|
|
472
502
|
});
|
|
503
|
+
return {
|
|
504
|
+
ok: true,
|
|
505
|
+
itemId: args.itemId,
|
|
506
|
+
mudou: mudou.map(([k]) => k),
|
|
507
|
+
instruction: "A peça continua na mesma lane e na mesma posição da fila, só com o que mudou. Texto de peça já postada o servidor recusa: o post na rede não muda junto.",
|
|
508
|
+
};
|
|
473
509
|
}
|
|
474
510
|
if (args.action === "voices") {
|
|
475
511
|
return await convexQuery("distributionMcp:mcpVoices", { sessionToken });
|
package/dist/tools/meta.js
CHANGED
|
@@ -66,15 +66,6 @@ export const metaSchema = z.object({
|
|
|
66
66
|
});
|
|
67
67
|
const APP_URL = "https://sapiensinteticos.com";
|
|
68
68
|
const FORMAT_GUIDE = {
|
|
69
|
-
post_social: {
|
|
70
|
-
description: "Post de texto curto/médio pra rede social (genérico).",
|
|
71
|
-
payloadHint: {
|
|
72
|
-
text: "string PT principal",
|
|
73
|
-
textEn: "string EN (opcional)",
|
|
74
|
-
hashtags: ["string"],
|
|
75
|
-
cta: "string opcional",
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
69
|
carrossel_ig: {
|
|
79
70
|
description: "Carrossel do Instagram (o modelo Feed tem 9 slides). O editor renderiza por TEMPLATE: cada slide é { id, templateId, slots, image }. O servidor NORMALIZA todo payload de carrossel: o shape cru antigo ({ title, body, imageUrl } por slide, caption e hashtags no topo) vira slots, foto e meta, mas sai sem layout escolhido, e campo fora do shape some. NÃO monte o payload na mão: use sapiens_pipeline action=generate_carousel_production (sourceId), que cria a production E preenche pelo motor da casa, no shape certo, seguindo o artigo. Depois afine com get_carousel/update_carousel (mesmo shape). O membro opera o carrossel dele pela sapiens_carrossel.",
|
|
80
71
|
payloadHint: {
|
|
@@ -98,6 +89,9 @@ const FORMAT_GUIDE = {
|
|
|
98
89
|
],
|
|
99
90
|
},
|
|
100
91
|
},
|
|
92
|
+
// post_social, shorts_yt e video_yt saíram do guia em out/2026 (vídeo vive no
|
|
93
|
+
// gerador de vídeo; post de texto perdeu o lugar). Seguem aceitos só como
|
|
94
|
+
// tolerância de validação, ver pipeline.ts.
|
|
101
95
|
// tirinha saiu do guia em ago/2026: peça aposentada (format vive só como
|
|
102
96
|
// tolerância de validação pra productions antigas, ver pipeline.ts).
|
|
103
97
|
musica: {
|
|
@@ -109,22 +103,6 @@ const FORMAT_GUIDE = {
|
|
|
109
103
|
audioUrl: "string opcional",
|
|
110
104
|
},
|
|
111
105
|
},
|
|
112
|
-
shorts_yt: {
|
|
113
|
-
description: "Roteiro pra short vertical de 30-60s.",
|
|
114
|
-
payloadHint: {
|
|
115
|
-
title: "string",
|
|
116
|
-
script: "string roteiro completo",
|
|
117
|
-
scenes: [{ visual: "string", voiceover: "string", duration: "number" }],
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
video_yt: {
|
|
121
|
-
description: "Vídeo longer-form.",
|
|
122
|
-
payloadHint: {
|
|
123
|
-
title: "string",
|
|
124
|
-
script: "string roteiro completo",
|
|
125
|
-
scenes: [{ visual: "string", voiceover: "string", duration: "number" }],
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
106
|
mega_grafico: {
|
|
129
107
|
description: "Pôster blueprint denso multi-seção (estilo Pop Chart), texto fiel via gpt-image-2. Tem runner próprio: propose_mega_grafico_plan / run_mega_grafico_full.",
|
|
130
108
|
payloadHint: {
|
package/dist/tools/pipeline.js
CHANGED
|
@@ -7,9 +7,6 @@ import { need } from "../schema.js";
|
|
|
7
7
|
// ou remover formato, mexa nos dois lados.
|
|
8
8
|
const FORMATS = [
|
|
9
9
|
"carrossel_ig",
|
|
10
|
-
"post_social",
|
|
11
|
-
"shorts_yt",
|
|
12
|
-
"video_yt",
|
|
13
10
|
"musica",
|
|
14
11
|
"aula",
|
|
15
12
|
];
|
|
@@ -20,6 +17,8 @@ const FORMATS = [
|
|
|
20
17
|
// seguem de pé). mega_grafico saiu do picker web em ago/2026 mas o fluxo MCP
|
|
21
18
|
// segue vivo e ADVERTIDO (propose_mega_grafico_plan / run_mega_grafico_full
|
|
22
19
|
// criam a production no servidor; o formato aqui é só tolerância de validação).
|
|
20
|
+
// post_social, shorts_yt e video_yt saíram do picker em out/2026: vídeo vive no
|
|
21
|
+
// gerador de vídeo (uma tela só) e o post de texto perdeu o lugar.
|
|
23
22
|
const LEGACY_FORMATS = [
|
|
24
23
|
"post_linkedin",
|
|
25
24
|
"post_twitter",
|
|
@@ -27,6 +26,9 @@ const LEGACY_FORMATS = [
|
|
|
27
26
|
"cena_visual",
|
|
28
27
|
"tirinha",
|
|
29
28
|
"mega_grafico",
|
|
29
|
+
"post_social",
|
|
30
|
+
"shorts_yt",
|
|
31
|
+
"video_yt",
|
|
30
32
|
];
|
|
31
33
|
const ACCEPTED_FORMATS = [...FORMATS, ...LEGACY_FORMATS];
|
|
32
34
|
const STATUSES = ["draft", "ready", "finalized"];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexAction, getSessionToken } from "../convexClient.js";
|
|
3
|
+
import { need } from "../schema.js";
|
|
4
|
+
/**
|
|
5
|
+
* sapiens_publicar: a peça do MEMBRO saindo pelas redes DELE.
|
|
6
|
+
*
|
|
7
|
+
* O mesmo painel lateral da tela (Publicar nas minhas redes), lido por quem não
|
|
8
|
+
* tem tela: as redes com o limite de texto e a régua de cada praça, onde a peça
|
|
9
|
+
* já está, pra onde vale mandar, a legenda de cada rede e o envio. Irmã da
|
|
10
|
+
* sapiens_distribution, que é a fila da CASA (admin); aqui é a conta da pessoa,
|
|
11
|
+
* pelo Distribution dela (assinatura ou convite).
|
|
12
|
+
*
|
|
13
|
+
* Sub-actions:
|
|
14
|
+
* - painel: as redes (conectada ou não, @, limite, régua). Com `handle`,
|
|
15
|
+
* a peça: onde já está, por rede, e a sugestão de onde mandar.
|
|
16
|
+
* - rascunho: guarda a legenda de UMA rede pra peça (vazia apaga).
|
|
17
|
+
* - publicar: manda agora ou agenda. Legenda em blocos: text (corpo), cta,
|
|
18
|
+
* hashtags. Cada rede leva o que cabe no limite dela.
|
|
19
|
+
* - lista: o que já saiu (os envios recentes, com o estado por rede).
|
|
20
|
+
* - conferir: pergunta ao fornecedor o que cada rede fez com um envio.
|
|
21
|
+
* - cancelar: cancela um envio agendado que ainda não saiu.
|
|
22
|
+
*
|
|
23
|
+
* Identidade SEMPRE pelo sessionToken. Sem custo em Sinapses: o teto é o do
|
|
24
|
+
* plano do Distribution (publicações por ciclo), e o servidor confere tudo de
|
|
25
|
+
* novo (dono da peça, rede, limite, tipo de mídia).
|
|
26
|
+
*/
|
|
27
|
+
export const publicarSchema = z.object({
|
|
28
|
+
action: z.enum(["painel", "rascunho", "publicar", "lista", "conferir", "cancelar"]),
|
|
29
|
+
handle: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("A peça (img_..., vid_...). Obrigatório em rascunho e publicar; em painel traz a peça junto."),
|
|
33
|
+
rede: z.string().optional().describe("rascunho: a rede (id do painel: instagram, tiktok, threads...)."),
|
|
34
|
+
legenda: z.string().optional().describe("rascunho: a legenda daquela rede. Vazia apaga o rascunho."),
|
|
35
|
+
redes: z.array(z.string()).optional().describe("publicar: as redes (ids do painel), só as conectadas."),
|
|
36
|
+
text: z.string().optional().describe("publicar: o CORPO da legenda. Ausente usa o título da peça."),
|
|
37
|
+
cta: z.string().optional().describe("publicar: a chamada, em bloco à parte. Sai antes do corpo quando não cabe."),
|
|
38
|
+
hashtags: z
|
|
39
|
+
.array(z.string())
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("publicar: as hashtags, sem #. São as primeiras a sair onde o limite aperta."),
|
|
42
|
+
legendas: z
|
|
43
|
+
.record(z.string(), z.string())
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("publicar: legenda própria por rede ({threads: '...'}), passa por cima dos blocos naquela rede."),
|
|
46
|
+
agendarPara: z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("publicar: data e hora ISO 8601 COM fuso (2026-10-08T19:00:00-03:00). Ausente = agora. Mínimo 5 min à frente."),
|
|
50
|
+
postId: z.string().optional().describe("conferir/cancelar: o id do envio, vindo da lista."),
|
|
51
|
+
});
|
|
52
|
+
export async function publicar(args) {
|
|
53
|
+
const sessionToken = getSessionToken();
|
|
54
|
+
switch (args.action) {
|
|
55
|
+
case "painel":
|
|
56
|
+
return await convexAction("uploadPost:mcpPainel", {
|
|
57
|
+
sessionToken,
|
|
58
|
+
...(args.handle ? { handle: args.handle } : {}),
|
|
59
|
+
});
|
|
60
|
+
case "rascunho":
|
|
61
|
+
return await convexAction("uploadPost:salvarRascunho", {
|
|
62
|
+
sessionToken,
|
|
63
|
+
handle: need(args.handle, "handle"),
|
|
64
|
+
rede: need(args.rede, "rede"),
|
|
65
|
+
legenda: args.legenda ?? "",
|
|
66
|
+
});
|
|
67
|
+
case "publicar": {
|
|
68
|
+
const redes = args.redes ?? [];
|
|
69
|
+
if (redes.length === 0)
|
|
70
|
+
throw new Error("Falta `redes`: em quais redes a peça sai (ids do painel).");
|
|
71
|
+
let scheduledAt;
|
|
72
|
+
if (args.agendarPara) {
|
|
73
|
+
scheduledAt = Date.parse(args.agendarPara);
|
|
74
|
+
if (!Number.isFinite(scheduledAt)) {
|
|
75
|
+
throw new Error("`agendarPara` não é uma data ISO válida. Ex: 2026-10-08T19:00:00-03:00.");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return await convexAction("uploadPost:publishOwn", {
|
|
79
|
+
sessionToken,
|
|
80
|
+
handle: need(args.handle, "handle"),
|
|
81
|
+
networks: redes,
|
|
82
|
+
...(args.text ? { text: args.text } : {}),
|
|
83
|
+
...(args.cta ? { cta: args.cta } : {}),
|
|
84
|
+
...(args.hashtags?.length ? { hashtags: args.hashtags } : {}),
|
|
85
|
+
...(args.legendas && Object.keys(args.legendas).length ? { captions: args.legendas } : {}),
|
|
86
|
+
...(scheduledAt !== undefined ? { scheduledAt } : {}),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
case "lista":
|
|
90
|
+
return await convexAction("uploadPost:mcpMinhasPublicacoes", { sessionToken });
|
|
91
|
+
case "conferir":
|
|
92
|
+
return await convexAction("uploadPost:conferirPublicacao", {
|
|
93
|
+
sessionToken,
|
|
94
|
+
postId: need(args.postId, "postId"),
|
|
95
|
+
});
|
|
96
|
+
case "cancelar":
|
|
97
|
+
return await convexAction("uploadPost:cancelarAgendado", {
|
|
98
|
+
sessionToken,
|
|
99
|
+
postId: need(args.postId, "postId"),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
package/dist/tools/video.js
CHANGED
|
@@ -407,7 +407,7 @@ export const videoSchema = z.object({
|
|
|
407
407
|
startImageId: z
|
|
408
408
|
.string()
|
|
409
409
|
.optional()
|
|
410
|
-
.describe("Frame inicial (i2v): generatedImages:_id da SUA galeria. Vira reference role 'start'."),
|
|
410
|
+
.describe("Frame inicial (i2v): generatedImages:_id da SUA galeria. Vira reference role 'start'. No Seedance 2.x e no H3 NÃO combina com referenceImage*/referenceVideoUrls: é um ou outro."),
|
|
411
411
|
startImageUrl: httpUrl()
|
|
412
412
|
.optional()
|
|
413
413
|
.describe("Frame inicial (i2v): url pública (Bunny/Convex/Wikimedia) de galeria/Acervo/personagem. Vira reference role 'start'."),
|
|
@@ -428,7 +428,8 @@ export const videoSchema = z.object({
|
|
|
428
428
|
"como imagens de REFERÊNCIA (role 'ref'). Diferente de startImageId: NÃO viram o 1º frame, guiam " +
|
|
429
429
|
"estilo/personagem/composição. Fluxo storyboard: gere a folha com sapiens_image " +
|
|
430
430
|
"templateSlug='storyboard-sapiens-v1' (ou -vertical-v1) e passe folha + personagem aqui. " +
|
|
431
|
-
"
|
|
431
|
+
"Referência e frame de borda são EXCLUSIVOS no H3 (endpoints diferentes) e na linha Seedance 2.0/Fast/Mini/2.5 (o provider recusa a mistura): mande refs OU startImage*/endImage*, não os dois. " +
|
|
432
|
+
"Quer a pessoa da foto no take? Mande a foto como referência e descreva o primeiro quadro no prompt."),
|
|
432
433
|
referenceImageUrls: z
|
|
433
434
|
.array(httpUrl())
|
|
434
435
|
.optional()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.87.0",
|
|
4
4
|
"mcpName": "com.sapiensinteticos/sapiens",
|
|
5
5
|
"description": "MCP server pra operar o Sapiens Sintéticos (sapiensinteticos.com) pelo Claude Code: gerar imagem, escrever artigo, voz, música e mais, na sua conta. Login pelo código de sapiensinteticos.com/conectar-claude.",
|
|
6
6
|
"type": "module",
|