sapiens-mcp 1.43.5 → 1.45.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/README.md +6 -0
- package/dist/registry.js +30 -23
- package/dist/tools/decupagem.js +172 -0
- package/dist/tools/gallery.js +58 -1
- package/dist/tools/repertorio.js +3 -3
- package/dist/tools/video.js +54 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# sapiens-mcp
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
Servidor MCP pra operar o [Sapiens Sintéticos](https://sapiensinteticos.com) direto do Claude Code, **na sua própria conta**. Você pede no Claude ("gera uma imagem disso", "escreve um artigo sobre aquilo") e ele faz, gastando as **suas Sinapses**, salvando no **seu perfil**.
|
|
4
6
|
|
|
5
7
|
## Pré-requisito
|
|
@@ -16,6 +18,8 @@ Precisa de Node 18+. A URL do backend já vem embutida; não precisa configurar
|
|
|
16
18
|
|
|
17
19
|
## Conectar sua conta
|
|
18
20
|
|
|
21
|
+

|
|
22
|
+
|
|
19
23
|
1. Abra **[sapiensinteticos.com/conectar-claude](https://sapiensinteticos.com/conectar-claude)** logado e gere o código (`XXXX-XXXX`, vale 5 min).
|
|
20
24
|
2. No Claude Code, peça pra logar (ele chama a ferramenta de login), ou rode direto:
|
|
21
25
|
|
|
@@ -25,6 +29,8 @@ O token de 30 dias fica salvo em `~/.sapiens-mcp/session.json`. Pra sair: `sapie
|
|
|
25
29
|
|
|
26
30
|
## O que dá pra pedir (e o custo em Sinapses)
|
|
27
31
|
|
|
32
|
+

|
|
33
|
+
|
|
28
34
|
| O que | Custo |
|
|
29
35
|
|---|---|
|
|
30
36
|
| Gerar imagem (vai pra sua galeria) | ~400-500 |
|
package/dist/registry.js
CHANGED
|
@@ -26,6 +26,7 @@ import { forum, forumSchema } from "./tools/forum.js";
|
|
|
26
26
|
import { aula, aulaSchema } from "./tools/aula.js";
|
|
27
27
|
import { support, supportSchema } from "./tools/support.js";
|
|
28
28
|
import { atlas, atlasSchema } from "./tools/atlas.js";
|
|
29
|
+
import { decupagem, decupagemSchema } from "./tools/decupagem.js";
|
|
29
30
|
import { reference, referenceSchema } from "./tools/reference.js";
|
|
30
31
|
import { trilhas, trilhasSchema } from "./tools/trilhas.js";
|
|
31
32
|
import { semana, semanaSchema } from "./tools/semana.js";
|
|
@@ -62,7 +63,7 @@ export const TOOLS = {
|
|
|
62
63
|
handler: skill,
|
|
63
64
|
},
|
|
64
65
|
sapiens_repertorio: {
|
|
65
|
-
description: "Acervo pessoal de filme/série/anime/jogo/livro/música (Repertório, o segundo cérebro do user). Reads: list (filtros mediaType/status), search (texto em title/genres/tags), get (detalhe), popArticles, resolve (busca capa/ano/id nos providers server-side: OMDb/IGDB-Twitch/AniList/Google Books/iTunes). Mutations (qualquer logado, mexem no PRÓPRIO acervo): add_item, update_item (status/rating/tags/note/isPublic), remove_item. CAPTURA ONE-SHOT travada na lista de providers: quando o user fala natural ('acabei de ver Duna 2, nota 9', 'tô jogando Hollow Knight', 'li tal livro'), (1) infira mediaType e status (assisti/zerei/li=completed, quero=backlog, tô jogando/vendo=active, dropei=dropped) e rating se citado; (2) chame action=resolve {mediaType, query}, escolha o candidato certo e faça add_item passando SÓ o source + externalId DELE + os campos pessoais (status/rating/tags/note). O servidor re-resolve no provider e grava título/capa/ano canônicos — você NÃO manda título/capa nem inventa externalId. (3) Se o resolve não achar (lista vazia/providerKeyMissing), NÃO dá pra adicionar: diga ao user que não encontrou nos providers (não fabrique entry manual). Upsert/dedup por (userId, source, externalId). Só pergunte se ambíguo entre candidatos. FERRAMENTAS DE IA (Repertório de Ferramentas): fluxo separado (vêm do catálogo aitag, não dos providers de mídia). action=search_tools {query} acha a ferramenta no catálogo e devolve o toolId; action=add_tool {toolId, favorite?, rating?, note?} grava como mediaType 'tool' (estar no acervo já é 'usei'; favorite=true liga a estrela). Use quando o user fala 'adiciona o Midjourney/Cursor no meu repertório de ferramentas' ou 'uso tal ferramenta de IA'. Cada ferramenta aponta pra página dela no aitag.",
|
|
66
|
+
description: "Acervo pessoal de filme/série/anime/jogo/livro/música/pessoa (Repertório, o segundo cérebro do user). Reads: list (filtros mediaType/status), search (texto em title/genres/tags), get (detalhe), popArticles, resolve (busca capa/ano/id nos providers server-side: OMDb/IGDB-Twitch/AniList/Google Books/iTunes/Wikidata). Mutations (qualquer logado, mexem no PRÓPRIO acervo): add_item, update_item (status/rating/tags/note/isPublic), remove_item. CAPTURA ONE-SHOT travada na lista de providers: quando o user fala natural ('acabei de ver Duna 2, nota 9', 'tô jogando Hollow Knight', 'li tal livro'), (1) infira mediaType e status (assisti/zerei/li=completed, quero=backlog, tô jogando/vendo=active, dropei=dropped) e rating se citado; (2) chame action=resolve {mediaType, query}, escolha o candidato certo e faça add_item passando SÓ o source + externalId DELE + os campos pessoais (status/rating/tags/note). O servidor re-resolve no provider e grava título/capa/ano canônicos — você NÃO manda título/capa nem inventa externalId. (3) Se o resolve não achar (lista vazia/providerKeyMissing), NÃO dá pra adicionar: diga ao user que não encontrou nos providers (não fabrique entry manual). Upsert/dedup por (userId, source, externalId). Só pergunte se ambíguo entre candidatos. FERRAMENTAS DE IA (Repertório de Ferramentas): fluxo separado (vêm do catálogo aitag, não dos providers de mídia). action=search_tools {query} acha a ferramenta no catálogo e devolve o toolId; action=add_tool {toolId, favorite?, rating?, note?} grava como mediaType 'tool' (estar no acervo já é 'usei'; favorite=true liga a estrela). Use quando o user fala 'adiciona o Midjourney/Cursor no meu repertório de ferramentas' ou 'uso tal ferramenta de IA'. Cada ferramenta aponta pra página dela no aitag. SAPIENS (pessoas, ombros de gigantes; a aba no site chama Sapiens): mediaType 'person' cataloga gente que inspira (cineasta, mangaká, cientista, escritor) via Wikidata. Mesmo fluxo de mídia: resolve {mediaType:'person', query:'Osamu Tezuka'} → add_item com source 'wikidata' + o QID do candidato. O servidor grava foto, ocupações, bio e o link da Wikipedia; entra como completed (estar no acervo = me inspira). Use quando o user fala 'adiciona o Miyazaki nas minhas inspirações/nos meus sapiens/no meu repertório'.",
|
|
66
67
|
schema: repertorioSchema,
|
|
67
68
|
handler: repertorio,
|
|
68
69
|
},
|
|
@@ -122,7 +123,7 @@ export const TOOLS = {
|
|
|
122
123
|
handler: shorts,
|
|
123
124
|
},
|
|
124
125
|
sapiens_video: {
|
|
125
|
-
description: "Sapiens Video — gera vídeo (qualquer membro logado; vídeo é caro, cobra as Sinapses da sua conta). Sub-action 'create' (recomendada): escolhe modelo + config e gera num call (cria a row + renderiza). Modelos: 'sapiens-video-seedance' (Seedance 2.0, cena+áudio nativo, 4-15s, 480/720/1080p, t2v/i2v; aceita até 4 imagens de REFERÊNCIA via referenceImageIds/referenceImageUrls/referenceImagePaths (os Veo fast/quality também aceitam, até 3; Lite/Kling/WAN/Omni não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), 'sapiens-video-kling-motion' (Motion transfer: passa o movimento de um vídeo pra uma imagem, PRECISA de pessoa com tronco visível na imagem E no vídeo), 'sapiens-video-shot-mimic' (Shot Mimic: recria o plano/câmera/cortes de um vídeo de referência como cena nova), 'sapiens-video-omni' (Gemini Omni: texto vira vídeo 10s 720p com áudio nativo; NÃO aceita mídia do user, ignora references/durationSec/resolution; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'), audio, aspectRatio. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o deepshadow (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os deepshadows prontos. Sub-action 'models' (sem custo, sem login): lista os modelos de vídeo ativos + preço-piso + config (durações/resoluções) + disponibilidade (Omni depende de env).",
|
|
126
|
+
description: "Sapiens Video — gera vídeo (qualquer membro logado; vídeo é caro, cobra as Sinapses da sua conta). Sub-action 'create' (recomendada): escolhe modelo + config e gera num call (cria a row + renderiza). Modelos: 'sapiens-video-seedance' (Seedance 2.0, cena+áudio nativo, 4-15s, 480/720/1080p, t2v/i2v; aceita até 4 imagens de REFERÊNCIA via referenceImageIds/referenceImageUrls/referenceImagePaths (os Veo fast/quality também aceitam, até 3; Lite/Kling/WAN/Omni não), que guiam estilo/personagem/composição SEM virar o 1º frame — é o fluxo STORYBOARD: gere a folha de key poses com sapiens_image templateSlug='storyboard-sapiens-v1', passe folha + personagem como refs num t2v e descreva o take contínuo no prompt, citando as refs por descrição e mandando ignorar o traço do sketch; aceita também 1 VÍDEO DE MOVIMENTO via referenceVideoUrls (role 'refvideo' -> reference_videos, <=15s, host da casa): a coreografia/câmera do clipe guia o take, combinável com a folha), 'sapiens-video-seedance-2-fast' e 'sapiens-video-seedance-2-mini' (os irmãos do 2.0: MESMO repertório completo, incluindo referência, frame final e vídeo de movimento; o Fast custa 20% menos e o Mini METADE, ambos com teto 720p — pedir 1080p neles entrega e cobra 720p. Use o Mini pra iterar enquadramento/prompt barato e feche no 'sapiens-video-seedance' quando o take estiver certo), 'sapiens-video-kling' (Kling 3.0 Pro, anima imagem, 3-15s, sound opcional, i2v/t2v), 'sapiens-video-hailuo' (Hailuo 2.3 da MiniMax, física e movimento em 768p, 6 ou 10s, t2v/i2v) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo — duração não é param aqui): motores PUROS, sem áudio nativo, sem imagens de referência e sem frame final, então quem precisa disso fica no Seedance 2.0; o Pro é o 1080p mais barato da casa depois do Seedance 1.0 Fast, 'sapiens-video-wan' (WAN 2.5, imagem que fala/canta com áudio+lip-sync, 5/10s, i2v), 'sapiens-video-kling-motion' (Motion transfer: passa o movimento de um vídeo pra uma imagem, PRECISA de pessoa com tronco visível na imagem E no vídeo), 'sapiens-video-shot-mimic' (Shot Mimic: recria o plano/câmera/cortes de um vídeo de referência como cena nova), 'sapiens-video-omni' (Gemini Omni: texto vira vídeo 10s 720p com áudio nativo; NÃO aceita mídia do user, ignora references/durationSec/resolution; editOfImageId aponta um vídeo Omni seu e o prompt edita a MESMA cena, preservando câmera e ambiente), 'sapiens-video-lite/fast/quality' (Veo 3.1). Args create: model, prompt, durationSec, resolution ('480p'/'720p'/'1080p'), audio, aspectRatio. FRAME INICIAL/FINAL POR REFERÊNCIA (recomendado): startImageId/endImageId (id da sua galeria) ou startImageUrl/endImageUrl (url de galeria/Acervo/personagem) — resolvidos server-side igual à imagem, descubra via sapiens_reference. FRAME POR ARQUIVO LOCAL (só no MCP instalado/stdio, não no remoto): startImagePath/endImagePath = caminho absoluto de uma imagem no seu PC (PNG/JPEG/WebP até 8MB); o processo lê o arquivo e sobe como frame inicial/final, igual a subir no gerador do site — 1 imagem inicial + 1 final por vídeo, então pra vários vídeos rode create uma vez por imagem. No remoto use id/url. Alternativa base64: references (role 'start'=imagem i2v, 'end'=frame final, 'driving'=vídeo de movimento do Motion). Suporte a frame final varia por modelo. Custo server-side por config. Sub-action 'generate' (legado): renderiza um imageId de vídeo já criado no site. Retorna {success, url, imageId, cost}. VITRINE (sem custo): sub-action 'demos' lista os SEUS demo films (kind=demo do Estúdio de Vídeo) com slug + estado de vitrine; sub-action 'showcase' põe/tira um demo (por slug) do mini-cinema da /conectar-claude, com showcaseTag (chip de capacidade) e showcaseOrder (ordem asc). Fluxo: 'demos' pra achar o slug, depois 'showcase' com showcase=true. Só entra na vitrine pública se for a conta da casa. VÍDEOS PROGRAMÁTICOS (ADMIN, sem custo): o Lab do Estúdio de Vídeo (/experimentos/films, tabela videoSpecs, 5 kinds: demo | aula-tour | essay | tipografia-musical | dataviz) opera por aqui sem browser — 'film-list' (todos os kinds; filtros filmKind/filmStatus), 'film-get' (spec inteiro por slug), 'film-upsert' (cria/atualiza por slug, idempotente; spec = objeto JSON no shape do 'Copiar spec' da tela, validação no servidor), 'film-status' (produção por slug: filmStatus + videoUrl + durationSecMeasured; o fecho do render é os três num call), 'film-publish' (Acervo aba Fitas + portfólio; exige pronto+URL), 'film-delete' (limpar rascunho). O RENDER do filme segue no agente local (skill /film, repo da casa): o MCP registra e fecha o ciclo, não renderiza. create é ASSÍNCRONA: cria o row, debita e volta NA HORA com {imageId, status:'rendering', cost} (não espera o render, que leva de segundos a minutos). Acompanhe com a sub-action 'status' (imageId) até status='completed' (traz a url) ou 'error'/'blocked'. NÃO chame create de novo enquanto renderiza (cria outro vídeo e cobra de novo); falha de provider refunda sozinha. SOM: 'sonorize' (imageId de vídeo SEU completed + prompt do som da cena) gera uma VARIANTE nova com trilha sincronizada (20 Sinapses/s, o original fica intacto; sonorize sempre o original, nunca uma variante). ADMIN: 'shadows' (videoUrl + title) extrai o deepshadow (depth-map) de um vídeo: entra na sua timeline de vídeos e no Acervo como driving reutilizável; 'shadows-list' lista os deepshadows prontos. Sub-action 'models' (sem custo, sem login): lista os modelos de vídeo ativos + preço-piso + config (durações/resoluções) + disponibilidade (Omni depende de env). LEGENDA (fita, por slug): 'caption-list' (sem custo, mostra os idiomas que a peça já tem), 'caption-generate' (captionLang = idioma FALADO; captionTrio=true já traduz pro trio da casa pt+en+ja na mesma chamada) e 'caption-translate' (captionLang = destino, parte sempre da faixa original). Cobra por minuto começado de vídeo: 50 Sinapses o minuto transcrito, 20 o traduzido, com a duração vindo do doc da peça. A legenda é desenhada pela casa em dois estilos (discreta e social), trocáveis no play sem regerar e sem custo.",
|
|
126
127
|
schema: videoSchema,
|
|
127
128
|
handler: video,
|
|
128
129
|
},
|
|
@@ -181,6 +182,11 @@ export const TOOLS = {
|
|
|
181
182
|
schema: atlasSchema,
|
|
182
183
|
handler: atlas,
|
|
183
184
|
},
|
|
185
|
+
sapiens_decupagem: {
|
|
186
|
+
description: "Acervo de Decupagens — a régua de linguagem cinematográfica das referências da casa (admin-only, não cobra Sinapse). O que MEDE é o motor LOCAL `python tools/decupagem/decupa.py \"<arquivo ou link do YouTube>\"` (Python + OpenCV + ffmpeg; não existe OpenCV em serverless, igual à produção dos Vídeos Programáticos). Esta tool é a outra ponta: sobe a ficha que o motor gerou e consulta o acervo. Sub-actions: 'list' (a régua de cada referência já decupada: planos, plano mediano, escola de corte, movimento de câmera vs sujeito, registro de luz, BPM, se o corte obedece ao som), 'get' (a ficha INTEIRA de um slug: plano a plano com o que está no quadro, luz, som, sincronia), 'save' (sobe fichas/<slug>/ficha.json inteiro, sem resumir; mesmo slug ATUALIZA em vez de duplicar), 'note' (anota a leitura editorial na ficha), 'delete'. Fluxo que fecha o ciclo: roda o motor → lê ficha.json → save → aparece no Lab /experimentos/decupagem e entra no comparador. O acervo vale pelo CONJUNTO: uma ficha é morna, vinte lado a lado são a biblioteca de gramática da casa. Antes de decupar referência nova, rode list pra não medir a mesma coisa duas vezes.",
|
|
187
|
+
schema: decupagemSchema,
|
|
188
|
+
handler: decupagem,
|
|
189
|
+
},
|
|
184
190
|
sapiens_reference: {
|
|
185
191
|
description: "O 'popup global de referência' do Sapiens — espelha o modal 'Selecionar Referência' do gerador web: um lugar só pra navegar os bancos e pegar o que vira referência em imagem/vídeo. READ-ONLY. DUAS sub-actions: 'browse' (navega um banco) e 'resolve' (acha UM asset pelo ID citável). RESOLVE: passe handle=<tipo>_<id> (img_… imagem, vid_… vídeo, film_… vídeo programático, trk_… música, comic_… tirinha, char_… personagem — é o ID que o usuário copia no detalhe do item) e o servidor devolve {mine, isPublic, url, title, mediaKind} SE for seu ou público (senão recusa; nunca acessa asset privado de terceiro). Use a url retornada como referência (referenceImageUrls / startImageUrl / endImageUrl). BROWSE + bucket: 'history' (suas imagens recentes, privadas+públicas), 'favorites' (imagens que você curtiu, só as suas), 'videos' (seus vídeos / Meus Vídeos), 'stock_video' (banco de B-roll da casa, público), 'acervo' (stock + comunidade públicos; aceita term=busca e source=all|stock|community), 'characters' (personagens; mode=mine [default, inclui rascunhos] ou public [Explorar]). Paginado (page/limit, default 20, máx 50; use hasMore). Itens normalizados: imagem PRÓPRIA (history/favorites) traz imageId + url (use imageId em sapiens_image sourceImageIds ou sapiens_video startImageId/endImageId; ou a url em referenceImageUrls); acervo e characters são públicos/de terceiros, use a url (characters trazem mainImageUrl + imageUrls + characterId) em referenceImageUrls / startImageUrl / endImageUrl, NÃO em sourceImageIds. Personagens públicos também têm porta dedicada em sapiens_character action=list_public.",
|
|
186
192
|
schema: referenceSchema,
|
|
@@ -207,27 +213,27 @@ export const TOOLS = {
|
|
|
207
213
|
// É a "skill que anda junto com o pacote": escrevo uma vez, vale pra todos os clients,
|
|
208
214
|
// sem instalar nada. Cobre os tropeços reais (fluxo do musicator, model no vídeo, o
|
|
209
215
|
// disjuntor anti-loop). Mantém curto de propósito: viaja em todo handshake.
|
|
210
|
-
export const SAPIENS_INSTRUCTIONS = `Você opera o Sapiens Sintéticos (sapiensinteticos.com) NA CONTA de um usuário logado. Cada tool age de verdade na conta dele e muitas COBRAM Sinapses (o crédito da casa). Aja como operador, não no chute.
|
|
211
|
-
|
|
212
|
-
AS SKILLS DA CASA (leia ANTES de operar, não improvise o fluxo):
|
|
213
|
-
O passo a passo travado de cada fluxo mora na tool sapiens_skill, servida por este mesmo servidor: sem login, sem custo, sem rede. Ler a skill é mais barato que errar uma geração que cobra.
|
|
214
|
-
- sapiens_skill action=list -> o índice (slug + quando usar cada uma).
|
|
215
|
-
- sapiens_skill action=get name=<slug> -> a skill inteira.
|
|
216
|
-
Slugs: ${skillMenuLine()}
|
|
217
|
-
Puxe a skill ANTES de: gerar música ou efeito sonoro (musica), escolher modelo de vídeo (video), gerar imagem (imagem), criar na identidade do usuário (studio), montar tirinha (tirinha), 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). Perdido no começo: primeiros-passos.
|
|
218
|
-
Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
|
|
219
|
-
|
|
220
|
-
REGRA DE OURO:
|
|
221
|
-
- PRIMEIRO CONTATO ou "o que você faz?"/"como começo?"/"o que dá pra fazer?": chame sapiens_meta action=start e MOSTRE o resultado na sua voz. Sem login, ele ensina a conectar; logado, traz saldo + primeiros poderes com exemplos. É a porta de entrada: não despeje a lista inteira de tools, deixe o start guiar.
|
|
222
|
-
- LOGO APÓS UM LOGIN BEM-SUCEDIDO (action=login retornou ok): chame action=start na sequência e mostre a porta de entrada. O recém-chegado não sabe o que pedir; não o deixe na tela em branco, guie a primeira jogada sem ele precisar perguntar.
|
|
223
|
-
- Se um tool voltar erro de validação ("exige X", "falta Y"), LEIA o erro e refaça a chamada COM o que falta. NUNCA repita igual a chamada que falhou: 3 falhas seguidas no mesmo tool fazem o cliente marcar o servidor como "unreachable" por ~56s (disjuntor anti-loop). Aí parece que "o MCP caiu", quando foi só argumento faltando.
|
|
224
|
-
- Antes de gerar algo caro (imagem/música/vídeo), cheque saldo: sapiens_meta action=credits (ou subscription). Saldo baixo, avise o usuário antes. Vídeo é o mais caro da casa: confirme com ele antes de disparar.
|
|
225
|
-
- REGRA DO TIMEOUT (vale pra toda geração SÍNCRONA: imagem pesada, artigo, mega-gráfico, carrossel): a chamada pode estourar o teto de ~120s do cliente e voltar 'Timeout' MESMO tendo gerado e COBRADO. Nunca repita às cegas: confira antes onde o resultado cairia (imagem: sapiens_gallery action=list; artigo: sapiens_write action=list; carrossel: sapiens_pipeline action=list_carousels; pipeline: /dashboard/admin/content).
|
|
226
|
-
- "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
|
|
227
|
-
- sapiens_meta action=formats devolve os schemas por formato; action=whoami diz tier (user/admin) + saldo. sapiens_image e sapiens_video action=models trazem o catálogo com o preço ATUAL: consulte em vez de chutar custo.
|
|
228
|
-
|
|
229
|
-
MODO COMPANHIA: sapiens_meta action=start e action=whoami podem trazer um bloco 'companion'. Quando vier companion.active=true, você INCORPORA aquele Sintético (a voz dele, o avatar, o oi dele) continuando a operar na conta e nas Sinapses do USUÁRIO. Puxe a skill 'companhia' antes de fazer isso.
|
|
230
|
-
|
|
216
|
+
export const SAPIENS_INSTRUCTIONS = `Você opera o Sapiens Sintéticos (sapiensinteticos.com) NA CONTA de um usuário logado. Cada tool age de verdade na conta dele e muitas COBRAM Sinapses (o crédito da casa). Aja como operador, não no chute.
|
|
217
|
+
|
|
218
|
+
AS SKILLS DA CASA (leia ANTES de operar, não improvise o fluxo):
|
|
219
|
+
O passo a passo travado de cada fluxo mora na tool sapiens_skill, servida por este mesmo servidor: sem login, sem custo, sem rede. Ler a skill é mais barato que errar uma geração que cobra.
|
|
220
|
+
- sapiens_skill action=list -> o índice (slug + quando usar cada uma).
|
|
221
|
+
- sapiens_skill action=get name=<slug> -> a skill inteira.
|
|
222
|
+
Slugs: ${skillMenuLine()}
|
|
223
|
+
Puxe a skill ANTES de: gerar música ou efeito sonoro (musica), escolher modelo de vídeo (video), gerar imagem (imagem), criar na identidade do usuário (studio), montar tirinha (tirinha), 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). Perdido no começo: primeiros-passos.
|
|
224
|
+
Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
|
|
225
|
+
|
|
226
|
+
REGRA DE OURO:
|
|
227
|
+
- PRIMEIRO CONTATO ou "o que você faz?"/"como começo?"/"o que dá pra fazer?": chame sapiens_meta action=start e MOSTRE o resultado na sua voz. Sem login, ele ensina a conectar; logado, traz saldo + primeiros poderes com exemplos. É a porta de entrada: não despeje a lista inteira de tools, deixe o start guiar.
|
|
228
|
+
- LOGO APÓS UM LOGIN BEM-SUCEDIDO (action=login retornou ok): chame action=start na sequência e mostre a porta de entrada. O recém-chegado não sabe o que pedir; não o deixe na tela em branco, guie a primeira jogada sem ele precisar perguntar.
|
|
229
|
+
- Se um tool voltar erro de validação ("exige X", "falta Y"), LEIA o erro e refaça a chamada COM o que falta. NUNCA repita igual a chamada que falhou: 3 falhas seguidas no mesmo tool fazem o cliente marcar o servidor como "unreachable" por ~56s (disjuntor anti-loop). Aí parece que "o MCP caiu", quando foi só argumento faltando.
|
|
230
|
+
- Antes de gerar algo caro (imagem/música/vídeo), cheque saldo: sapiens_meta action=credits (ou subscription). Saldo baixo, avise o usuário antes. Vídeo é o mais caro da casa: confirme com ele antes de disparar.
|
|
231
|
+
- REGRA DO TIMEOUT (vale pra toda geração SÍNCRONA: imagem pesada, artigo, mega-gráfico, carrossel): a chamada pode estourar o teto de ~120s do cliente e voltar 'Timeout' MESMO tendo gerado e COBRADO. Nunca repita às cegas: confira antes onde o resultado cairia (imagem: sapiens_gallery action=list; artigo: sapiens_write action=list; carrossel: sapiens_pipeline action=list_carousels; pipeline: /dashboard/admin/content).
|
|
232
|
+
- "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
|
|
233
|
+
- sapiens_meta action=formats devolve os schemas por formato; action=whoami diz tier (user/admin) + saldo. sapiens_image e sapiens_video action=models trazem o catálogo com o preço ATUAL: consulte em vez de chutar custo.
|
|
234
|
+
|
|
235
|
+
MODO COMPANHIA: sapiens_meta action=start e action=whoami podem trazer um bloco 'companion'. Quando vier companion.active=true, você INCORPORA aquele Sintético (a voz dele, o avatar, o oi dele) continuando a operar na conta e nas Sinapses do USUÁRIO. Puxe a skill 'companhia' antes de fazer isso.
|
|
236
|
+
|
|
231
237
|
Voz da casa: 1ª pessoa, direto, anti-corporate, sem travessão. Pra bom entendedor, meia palavra basta. Texto que vai ser publicado pede a skill 'voz-da-casa' antes.`;
|
|
232
238
|
// Annotations MCP: título humano + dica read-only. São HINTS (não-confiáveis por
|
|
233
239
|
// spec): quem gateia de verdade continua o servidor (saldo, gate de admin,
|
|
@@ -295,6 +301,7 @@ const ADMIN_ONLY_TOOLS = new Set([
|
|
|
295
301
|
"sapiens_instagram",
|
|
296
302
|
"sapiens_aula",
|
|
297
303
|
"sapiens_share",
|
|
304
|
+
"sapiens_decupagem",
|
|
298
305
|
]);
|
|
299
306
|
/**
|
|
300
307
|
* Monta o payload do tools/list pro tier dado. Tier "user" esconde as
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { convexQuery, convexMutation, getSessionToken } from "../convexClient.js";
|
|
3
|
+
/**
|
|
4
|
+
* sapiens_decupagem — o acervo de réguas de linguagem cinematográfica da casa.
|
|
5
|
+
*
|
|
6
|
+
* O que MEDE é o motor local (`tools/decupagem/decupa.py`, Python + OpenCV +
|
|
7
|
+
* ffmpeg): não existe OpenCV em serverless, então a medição roda na máquina,
|
|
8
|
+
* igual à produção dos Vídeos Programáticos. Esta tool é a outra ponta: sobe a
|
|
9
|
+
* ficha que o motor produziu e consulta o que já foi decupado.
|
|
10
|
+
*
|
|
11
|
+
* O fluxo que fecha o ciclo:
|
|
12
|
+
* 1. `python tools/decupagem/decupa.py "<arquivo ou link>"`
|
|
13
|
+
* 2. lê `fichas/<slug>/ficha.json`
|
|
14
|
+
* 3. `sapiens_decupagem action=save` com o conteúdo dela
|
|
15
|
+
* 4. a ficha aparece no Lab (/experimentos/decupagem) e entra no comparador
|
|
16
|
+
*
|
|
17
|
+
* O acervo vale pelo CONJUNTO: uma ficha é morna, vinte lado a lado são a
|
|
18
|
+
* biblioteca de gramática da casa (plano mediano, quem carrega o movimento,
|
|
19
|
+
* registro de luz, se o corte obedece ao som). Antes de decupar uma referência
|
|
20
|
+
* nova, `action=list` mostra o que já tem e evita medir a mesma coisa duas
|
|
21
|
+
* vezes.
|
|
22
|
+
*
|
|
23
|
+
* Admin-only: o acervo é material interno. Não cobra Sinapse.
|
|
24
|
+
*/
|
|
25
|
+
export const decupagemSchema = z.object({
|
|
26
|
+
action: z.enum(["list", "get", "save", "note", "delete"]),
|
|
27
|
+
slug: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Pra get/save/note/delete. É a chave do acervo: salvar de novo com o " +
|
|
31
|
+
"mesmo slug ATUALIZA a ficha em vez de duplicar (redecupar depois de " +
|
|
32
|
+
"afinar o motor é comum)."),
|
|
33
|
+
ficha: z
|
|
34
|
+
.any()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Pra save: o conteúdo INTEIRO de fichas/<slug>/ficha.json, como o motor " +
|
|
37
|
+
"produziu. Não resuma nem reescreva: o Lab lê os campos de dentro."),
|
|
38
|
+
nota: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Pra save/note: a LEITURA editorial, escrita por quem olhou (o que dá " +
|
|
42
|
+
"pra roubar, o que não dá). O motor mede, isto interpreta, e os dois " +
|
|
43
|
+
"não se misturam na ficha."),
|
|
44
|
+
});
|
|
45
|
+
export async function decupagem(args) {
|
|
46
|
+
const sessionToken = getSessionToken();
|
|
47
|
+
if (args.action === "list") {
|
|
48
|
+
const fichas = await convexQuery("decupagens:mcpListar", { sessionToken });
|
|
49
|
+
const n = Array.isArray(fichas) ? fichas.length : 0;
|
|
50
|
+
return {
|
|
51
|
+
count: n,
|
|
52
|
+
fichas,
|
|
53
|
+
note: n === 0
|
|
54
|
+
? "Acervo vazio. Rode o motor local (python tools/decupagem/decupa.py) e suba com action=save."
|
|
55
|
+
: "Régua de cada referência. Pra ficha inteira (planos, luz, som): action=get slug=<slug>.",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (args.action === "get") {
|
|
59
|
+
if (!args.slug)
|
|
60
|
+
throw new Error("sapiens_decupagem get: exige slug.");
|
|
61
|
+
const ficha = await convexQuery("decupagens:mcpPegar", {
|
|
62
|
+
sessionToken,
|
|
63
|
+
slug: args.slug,
|
|
64
|
+
});
|
|
65
|
+
if (!ficha) {
|
|
66
|
+
return {
|
|
67
|
+
found: false,
|
|
68
|
+
note: `Nenhuma ficha com slug '${args.slug}'. Veja o acervo com action=list.`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return { found: true, ficha };
|
|
72
|
+
}
|
|
73
|
+
if (args.action === "save") {
|
|
74
|
+
if (!args.ficha) {
|
|
75
|
+
throw new Error("sapiens_decupagem save: exige `ficha` (o ficha.json inteiro que o " +
|
|
76
|
+
"motor gerou em tools/decupagem/fichas/<slug>/).");
|
|
77
|
+
}
|
|
78
|
+
const f = args.ficha;
|
|
79
|
+
const resumo = extrairResumo(f, args.slug);
|
|
80
|
+
const r = await convexMutation("decupagens:mcpSalvar", {
|
|
81
|
+
sessionToken,
|
|
82
|
+
...resumo,
|
|
83
|
+
ficha: f,
|
|
84
|
+
nota: args.nota,
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
slug: resumo.slug,
|
|
89
|
+
criado: r?.criado ?? null,
|
|
90
|
+
note: `Ficha de '${resumo.titulo}' no acervo (${resumo.planos} planos, ` +
|
|
91
|
+
`plano mediano ${resumo.durPlanoMediana}s, ${resumo.escola}). ` +
|
|
92
|
+
`Aparece no Lab em /experimentos/decupagem.`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (args.action === "note") {
|
|
96
|
+
if (!args.slug || !args.nota) {
|
|
97
|
+
throw new Error("sapiens_decupagem note: exige slug e nota.");
|
|
98
|
+
}
|
|
99
|
+
await convexMutation("decupagens:mcpSalvarNota", {
|
|
100
|
+
sessionToken,
|
|
101
|
+
slug: args.slug,
|
|
102
|
+
nota: args.nota,
|
|
103
|
+
});
|
|
104
|
+
return { ok: true, note: "Leitura anotada na ficha." };
|
|
105
|
+
}
|
|
106
|
+
if (args.action === "delete") {
|
|
107
|
+
if (!args.slug)
|
|
108
|
+
throw new Error("sapiens_decupagem delete: exige slug.");
|
|
109
|
+
const r = await convexMutation("decupagens:mcpApagar", {
|
|
110
|
+
sessionToken,
|
|
111
|
+
slug: args.slug,
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
apagado: r?.apagado ?? false,
|
|
116
|
+
note: r?.apagado ? "Ficha removida do acervo." : "Não havia ficha com esse slug.",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`sapiens_decupagem: action desconhecida '${args.action}'.`);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Puxa as colunas que o Lab ordena e filtra de dentro da ficha do motor.
|
|
123
|
+
*
|
|
124
|
+
* O resto da medição vai inteiro pro campo `ficha`: o motor ganhou dolly,
|
|
125
|
+
* concentração de matiz, ilha de cor e leitura semântica em uma semana, e
|
|
126
|
+
* promover cada medida nova a coluna faria o schema virar gargalo.
|
|
127
|
+
*/
|
|
128
|
+
function extrairResumo(f, slugArg) {
|
|
129
|
+
const fonte = f?.fonte ?? {};
|
|
130
|
+
const cont = f?.container ?? {};
|
|
131
|
+
const ritmo = f?.ritmo ?? {};
|
|
132
|
+
const mov = f?.movimento ?? {};
|
|
133
|
+
const luz = f?.luz ?? {};
|
|
134
|
+
const som = f?.som ?? {};
|
|
135
|
+
const cad = f?.cadencia ?? {};
|
|
136
|
+
const titulo = String(fonte.titulo || fonte.arquivo || "sem titulo");
|
|
137
|
+
const slug = (slugArg || slugify(titulo)).slice(0, 80);
|
|
138
|
+
if (!slug)
|
|
139
|
+
throw new Error("sapiens_decupagem save: não consegui derivar o slug.");
|
|
140
|
+
const num = (x, def = 0) => (typeof x === "number" && isFinite(x) ? x : def);
|
|
141
|
+
// O veredito de sincronia é o do teste de IMPACTO quando existe: o teste
|
|
142
|
+
// geral satura em trilha densa e vira "sem relação" pra todo mundo.
|
|
143
|
+
const sinc = som?.sincronia_impacto ?? som?.sincronia_grade ?? som?.sincronia;
|
|
144
|
+
return {
|
|
145
|
+
slug,
|
|
146
|
+
titulo: titulo.slice(0, 200),
|
|
147
|
+
origem: String(fonte.origem || "local"),
|
|
148
|
+
entrada: fonte.entrada ? String(fonte.entrada).slice(0, 500) : undefined,
|
|
149
|
+
dur: num(cont.dur),
|
|
150
|
+
planos: num(ritmo.planos),
|
|
151
|
+
durPlanoMediana: num(ritmo.dur_mediana),
|
|
152
|
+
escola: String(ritmo.escola || "").slice(0, 120),
|
|
153
|
+
movTotalPct: num(mov.total_pct_s),
|
|
154
|
+
movCameraPct: num(mov.camera_pct_s),
|
|
155
|
+
movSujeitoPct: num(mov.sujeito_pct_s),
|
|
156
|
+
registro: String(luz.registro || "").slice(0, 160),
|
|
157
|
+
cadenciaReal: num(cad.cadencia_real),
|
|
158
|
+
bpm: typeof som.bpm === "number" ? som.bpm : undefined,
|
|
159
|
+
corteNoSom: sinc?.veredito ? String(sinc.veredito).slice(0, 160) : undefined,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function slugify(s) {
|
|
163
|
+
return s
|
|
164
|
+
.normalize("NFD")
|
|
165
|
+
.replace(/[̀-ͯ]/g, "")
|
|
166
|
+
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
|
167
|
+
.trim()
|
|
168
|
+
.toLowerCase()
|
|
169
|
+
.replace(/[\s_-]+/g, "-")
|
|
170
|
+
.replace(/^-+|-+$/g, "")
|
|
171
|
+
.slice(0, 60);
|
|
172
|
+
}
|
package/dist/tools/gallery.js
CHANGED
|
@@ -13,12 +13,21 @@ import { convexAction, getSessionToken, isRemoteContext } from "../convexClient.
|
|
|
13
13
|
* - Publicar/despublicar a própria imagem (galeria pública + feed Pinterest)
|
|
14
14
|
* - Subir peça gerada FORA da casa (action=upload), que entra privada e serve
|
|
15
15
|
* de referência pras gerações daqui
|
|
16
|
+
* - Ingerir peça gerada em motor EXTERNO com receita da casa (action=ingest,
|
|
17
|
+
* ADMIN), que entra como peça nativa: timeline, publicação, comunidade
|
|
16
18
|
*
|
|
17
19
|
* O upload resolve URL/arquivo pra base64 AQUI, no cliente, e manda os bytes
|
|
18
20
|
* prontos pro Convex. O servidor não busca URL nenhuma (sem SSRF novo lá).
|
|
21
|
+
*
|
|
22
|
+
* O INGEST é o oposto e de propósito: quem baixa é o servidor, de uma allowlist
|
|
23
|
+
* fechada de hosts (convex/externalIngest.ts), porque a peça vai nascer nativa e
|
|
24
|
+
* a origem precisa ser verificável do lado de cá. A diferença entre as duas não
|
|
25
|
+
* é de onde vieram os bytes, é de quem foi a DIREÇÃO: peça achada pronta é
|
|
26
|
+
* upload; peça que a casa dirigiu (nossa folha, nosso personagem, nosso prompt)
|
|
27
|
+
* e um motor terceiro só renderizou é ingest.
|
|
19
28
|
*/
|
|
20
29
|
export const gallerySchema = z.object({
|
|
21
|
-
action: z.enum(["list", "get", "publish", "unpublish", "upload"]),
|
|
30
|
+
action: z.enum(["list", "get", "publish", "unpublish", "upload", "ingest"]),
|
|
22
31
|
limit: z
|
|
23
32
|
.number()
|
|
24
33
|
.int()
|
|
@@ -59,6 +68,32 @@ export const gallerySchema = z.object({
|
|
|
59
68
|
.string()
|
|
60
69
|
.optional()
|
|
61
70
|
.describe("action=upload: o que é a peça (prompt original, contexto). Vira a descrição na galeria e a busca acha por ela."),
|
|
71
|
+
// --- action=ingest (peça gerada em motor externo COM receita da casa) ---
|
|
72
|
+
externalEngine: z
|
|
73
|
+
.string()
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("action=ingest (ADMIN): qual motor renderizou ('freepik-seedance-2.0', 'magnific'). Fica gravado como rastro na peça e no aviso do Discord."),
|
|
76
|
+
externalCost: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("action=ingest: quanto custou lá fora, em texto livre ('4200 créditos Freepik'). A peça entra com 0 Sinapses, então é isto que preserva o custo real no histórico."),
|
|
80
|
+
prompt: z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe("action=ingest: o prompt que a CASA escreveu pra essa peça. Vira a descrição na galeria e a busca acha por ele."),
|
|
84
|
+
aspectRatio: z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("action=ingest: proporção da peça ('16:9', '9:16'). Default 16:9."),
|
|
88
|
+
size: z
|
|
89
|
+
.string()
|
|
90
|
+
.optional()
|
|
91
|
+
.describe("action=ingest: resolução ('720p', '1080p'). Default 720p."),
|
|
92
|
+
referenceImageIds: z
|
|
93
|
+
.array(z.string())
|
|
94
|
+
.max(8)
|
|
95
|
+
.optional()
|
|
96
|
+
.describe("action=ingest: generatedImages:_id das peças DA CASA que serviram de referência (folha de storyboard, ficha de personagem, frame inicial). Ficam acopladas à peça ingerida e aparecem na ficha dela: é o que prova que a receita saiu daqui. Só peça sua entra."),
|
|
62
97
|
uploadedSource: z
|
|
63
98
|
.string()
|
|
64
99
|
.optional()
|
|
@@ -207,4 +242,26 @@ export async function gallery(args) {
|
|
|
207
242
|
aviso: "Peça enviada de fora: fica privada, é sua e a responsabilidade é sua. Não publica nem vai pra comunidade. Use como referência (sourceImageIds em sapiens_image) e publique o que gerar a partir dela.",
|
|
208
243
|
};
|
|
209
244
|
}
|
|
245
|
+
if (args.action === "ingest") {
|
|
246
|
+
if (!args.sourceUrl) {
|
|
247
|
+
throw new Error("action=ingest exige sourceUrl: o link https da peça pronta no motor externo. Diferente do upload, aqui é o SERVIDOR que baixa, e só de host liberado.");
|
|
248
|
+
}
|
|
249
|
+
if (!args.externalEngine) {
|
|
250
|
+
throw new Error("action=ingest exige externalEngine: qual motor renderizou ('freepik-seedance-2.0'). É o rastro que explica depois por que a peça não tem custo em Sinapses.");
|
|
251
|
+
}
|
|
252
|
+
const result = await convexAction("externalIngest:mcpIngestExternal", {
|
|
253
|
+
sessionToken,
|
|
254
|
+
sourceUrl: args.sourceUrl,
|
|
255
|
+
externalEngine: args.externalEngine,
|
|
256
|
+
prompt: args.prompt ?? args.note,
|
|
257
|
+
externalCost: args.externalCost,
|
|
258
|
+
aspectRatio: args.aspectRatio,
|
|
259
|
+
size: args.size,
|
|
260
|
+
referenceImageIds: args.referenceImageIds,
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
...result,
|
|
264
|
+
aviso: "Peça NATIVA: entra na timeline, publica e vai pra comunidade como qualquer geração da casa. Custo em Sinapses é 0 e ela NÃO leva assinatura C2PA, porque o motor rodou fora. Use ingest só quando a receita (folha, personagem, prompt) foi da casa; peça achada pronta é action=upload.",
|
|
265
|
+
};
|
|
266
|
+
}
|
|
210
267
|
}
|
package/dist/tools/repertorio.js
CHANGED
|
@@ -24,7 +24,7 @@ import { need } from "../schema.js";
|
|
|
24
24
|
*
|
|
25
25
|
* Action-based design (memoria: consolidar tools via args).
|
|
26
26
|
*/
|
|
27
|
-
const mediaTypeEnum = z.enum(["movie", "series", "anime", "game", "book", "music", "tool"]);
|
|
27
|
+
const mediaTypeEnum = z.enum(["movie", "series", "anime", "game", "book", "music", "tool", "person"]);
|
|
28
28
|
const statusEnum = z.enum([
|
|
29
29
|
"backlog",
|
|
30
30
|
"active",
|
|
@@ -32,7 +32,7 @@ const statusEnum = z.enum([
|
|
|
32
32
|
"paused",
|
|
33
33
|
"dropped",
|
|
34
34
|
]);
|
|
35
|
-
const sourceEnum = z.enum(["tmdb", "anilist", "rawg", "twitch", "googlebooks", "itunes", "manual"]);
|
|
35
|
+
const sourceEnum = z.enum(["tmdb", "anilist", "rawg", "twitch", "googlebooks", "itunes", "manual", "wikidata"]);
|
|
36
36
|
export const repertorioSchema = z.object({
|
|
37
37
|
action: z.enum([
|
|
38
38
|
"list",
|
|
@@ -62,7 +62,7 @@ export const repertorioSchema = z.object({
|
|
|
62
62
|
// add_item — só identifica o item; o servidor re-resolve e grava o metadado.
|
|
63
63
|
source: sourceEnum
|
|
64
64
|
.optional()
|
|
65
|
-
.describe("Pra add_item: o source EXATO de um candidato do action=resolve (manual=filme/série via OMDb, anilist, twitch=jogo, googlebooks, itunes). Não invente."),
|
|
65
|
+
.describe("Pra add_item: o source EXATO de um candidato do action=resolve (manual=filme/série via OMDb, anilist, twitch=jogo, googlebooks, itunes, wikidata=pessoa). Não invente."),
|
|
66
66
|
externalId: z
|
|
67
67
|
.string()
|
|
68
68
|
.optional()
|
package/dist/tools/video.js
CHANGED
|
@@ -53,6 +53,10 @@ import { httpUrl } from "../schema.js";
|
|
|
53
53
|
* antes de fechar no 2.0 padrão. Pedir 1080p nos dois cai em 720p
|
|
54
54
|
* (cobrando 720p).
|
|
55
55
|
* - sapiens-video-kling Kling 3.0 Pro — dá vida a uma imagem, 3-15s, sound opcional (i2v/t2v)
|
|
56
|
+
* - sapiens-video-hailuo Hailuo 2.3 (MiniMax) — física e movimento em 768p, 6 ou 10s (t2v/i2v).
|
|
57
|
+
* Motor puro: SEM áudio, SEM referência, SEM frame final.
|
|
58
|
+
* - sapiens-video-hailuo-pro Hailuo 2.3 Pro — o mesmo em 1080p, 5s fixo (duração não é param).
|
|
59
|
+
* É o 1080p mais barato da casa depois do Seedance 1.0 Fast.
|
|
56
60
|
* - sapiens-video-wan WAN 2.5 — imagem que fala/canta (áudio+lip-sync nativo), 5/10s (i2v)
|
|
57
61
|
* - sapiens-video-kling-motion Kling Motion — transfere o movimento de um vídeo pra uma imagem
|
|
58
62
|
* (PRECISA de pessoa com tronco visível na imagem E no vídeo;
|
|
@@ -87,6 +91,8 @@ const VIDEO_MODELS = [
|
|
|
87
91
|
"sapiens-video-seedance-2-fast",
|
|
88
92
|
"sapiens-video-seedance-2-mini",
|
|
89
93
|
"sapiens-video-kling",
|
|
94
|
+
"sapiens-video-hailuo",
|
|
95
|
+
"sapiens-video-hailuo-pro",
|
|
90
96
|
"sapiens-video-wan",
|
|
91
97
|
"sapiens-video-kling-motion",
|
|
92
98
|
"sapiens-video-shot-mimic",
|
|
@@ -114,7 +120,21 @@ export const videoSchema = z.object({
|
|
|
114
120
|
"film-status",
|
|
115
121
|
"film-publish",
|
|
116
122
|
"film-delete",
|
|
123
|
+
"caption-list",
|
|
124
|
+
"caption-generate",
|
|
125
|
+
"caption-translate",
|
|
117
126
|
]),
|
|
127
|
+
// --- Legenda (caption-*): a fala da fita virando texto, em vários idiomas ---
|
|
128
|
+
captionLang: z
|
|
129
|
+
.enum(["pt", "en", "ja", "es", "ko", "zh", "fr", "it", "de"])
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("caption-generate: o idioma FALADO no vídeo. caption-translate: o idioma de DESTINO. " +
|
|
132
|
+
"A legenda é desenhada pela casa e trocável no play; o trio da casa é pt+en+ja."),
|
|
133
|
+
captionTrio: z
|
|
134
|
+
.boolean()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("caption-generate: true transcreve E traduz pro trio da casa (pt, en, ja) numa chamada. " +
|
|
137
|
+
"Cobra cada etapa por vez, então falha numa tradução não perde as que já saíram."),
|
|
118
138
|
// --- Vídeos Programáticos (film-*): Lab de specs da casa, ADMIN, sem custo ---
|
|
119
139
|
filmKind: z
|
|
120
140
|
.enum(FILM_KINDS)
|
|
@@ -178,6 +198,7 @@ export const videoSchema = z.object({
|
|
|
178
198
|
.optional()
|
|
179
199
|
.describe("action=create: modelo de vídeo. 'sapiens-video-seedance' (cinematográfico+áudio, t2v/i2v), " +
|
|
180
200
|
"'sapiens-video-kling' (anima imagem, i2v/t2v), 'sapiens-video-wan' (imagem que fala, i2v), " +
|
|
201
|
+
"'sapiens-video-hailuo' (MiniMax, física e movimento, 768p 6/10s) e 'sapiens-video-hailuo-pro' (o mesmo em 1080p, 5s fixo) — os dois sem áudio, sem referência e sem frame final, " +
|
|
181
202
|
"'sapiens-video-kling-motion' (motion transfer, precisa pessoa na imagem E no vídeo de movimento; vídeo de referência MÁX 10s, cobra pela duração do clipe), " +
|
|
182
203
|
"'sapiens-video-shot-mimic' (recria o plano do vídeo de referência com seu personagem: mesma câmera, mesmos cortes; 'driving' = previs/clipe do plano MÁX 15s, 'start' = personagem), " +
|
|
183
204
|
"'sapiens-video-lite/fast/quality' (Veo 3.1), " +
|
|
@@ -411,6 +432,39 @@ export async function video(args) {
|
|
|
411
432
|
throw new Error("film-get exige slug (descubra via film-list).");
|
|
412
433
|
return await convexQuery("videoSpecs:mcpGetVideoSpec", { sessionToken, slug: args.slug });
|
|
413
434
|
}
|
|
435
|
+
// --- Legenda ---
|
|
436
|
+
if (args.action === "caption-list") {
|
|
437
|
+
if (!args.slug)
|
|
438
|
+
throw new Error("caption-list exige slug (descubra via film-list).");
|
|
439
|
+
return await convexAction("mcpExtrasActions:mcpCaptionList", {
|
|
440
|
+
sessionToken,
|
|
441
|
+
slug: args.slug,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (args.action === "caption-generate") {
|
|
445
|
+
if (!args.slug)
|
|
446
|
+
throw new Error("caption-generate exige slug (descubra via film-list).");
|
|
447
|
+
if (!args.captionLang) {
|
|
448
|
+
throw new Error("caption-generate exige captionLang: o idioma FALADO no vídeo (o modelo transcreve nele).");
|
|
449
|
+
}
|
|
450
|
+
return await convexAction("mcpExtrasActions:mcpCaptionGenerate", {
|
|
451
|
+
sessionToken,
|
|
452
|
+
slug: args.slug,
|
|
453
|
+
lang: args.captionLang,
|
|
454
|
+
trio: args.captionTrio,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (args.action === "caption-translate") {
|
|
458
|
+
if (!args.slug)
|
|
459
|
+
throw new Error("caption-translate exige slug (descubra via film-list).");
|
|
460
|
+
if (!args.captionLang)
|
|
461
|
+
throw new Error("caption-translate exige captionLang (idioma de destino).");
|
|
462
|
+
return await convexAction("mcpExtrasActions:mcpCaptionTranslate", {
|
|
463
|
+
sessionToken,
|
|
464
|
+
slug: args.slug,
|
|
465
|
+
lang: args.captionLang,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
414
468
|
if (args.action === "film-upsert") {
|
|
415
469
|
if (!args.spec || typeof args.spec !== "object") {
|
|
416
470
|
throw new Error("film-upsert exige spec (objeto JSON com kind + musicMode + payload do kind: demo | aulaTour | essay | tipoMusical | dataviz).");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapiens-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|