sapiens-mcp 1.81.0 → 1.82.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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Borderless (Sapiens Sintéticos)
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Borderless (Sapiens Sintéticos)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,98 +1,98 @@
1
- # sapiens-mcp
2
-
3
- ![Helen Ailith numa mesa escura, antebracos vestidos por um exoesqueleto de luz verde, movendo placas de luz com as maos abertas](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430717302_e8p3i4.webp)
4
-
5
- <p align="center">
6
- <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-pt-3e96346c.mp4"><img src="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-pt-497bacfa.jpg" width="720" alt="O sapiens-mcp em 15 segundos: você fala, sua IA executa"></a><br>
7
- <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-pt-3e96346c.mp4"><b>Assiste em 15 segundos, com som</b></a> · <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-9x16-pt-b5b02542.mp4">corte vertical</a> · <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-en-e17d0673.mp4">in English</a>
8
- </p>
9
-
10
- 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**.
11
-
12
- ## Pré-requisito
13
-
14
- Uma conta no Sapiens Sintéticos. Conta e login acontecem só no site (Google ou email), nunca por aqui. Não tem conta? Crie em [sapiensinteticos.com](https://sapiensinteticos.com).
15
-
16
- ## Instalar (Claude Code)
17
-
18
- ```bash
19
- claude mcp add sapiens -- npx -y sapiens-mcp
20
- ```
21
-
22
- Precisa de Node 18+. A URL do backend já vem embutida; não precisa configurar nada.
23
-
24
- ## Conectar sua conta
25
-
26
- ![Helen segurando uma chave de luz verde entre duas portas: um retangulo de luz sem moldura e um terminal fisico](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430826064_avwjib.webp)
27
-
28
- 1. Abra **[sapiensinteticos.com/conectar-claude](https://sapiensinteticos.com/conectar-claude)** logado e gere o código (`XXXX-XXXX`, vale 5 min).
29
- 2. No Claude Code, peça pra logar (ele chama a ferramenta de login), ou rode direto:
30
-
31
- `sapiens_meta` com `action: "login"` e `code: "XXXX-XXXX"`
32
-
33
- O token vale 90 dias e renova sozinha a cada uso; fica salvo em `~/.sapiens-mcp/session.json`. Pra sair: `sapiens_meta action=logout`, que revoga a chave no servidor e apaga o arquivo local.
34
-
35
- ## O que dá pra pedir (e o custo em Sinapses)
36
-
37
- ![Helen com um braco estendido, orbitada por uma lente, rolos de filme, um vinil, um caderno aberto e um microfone de estudio](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430870745_waml5a.webp)
38
-
39
- | O que | Custo |
40
- |---|---|
41
- | Gerar imagem (vai pra sua galeria) | ~400-500 |
42
- | Escrever artigo na voz Sapiens (no seu perfil) | 400 |
43
- | Voz / narração (Helen TTS) | 500 |
44
- | Letra de música / render (Musicator) | 300 / 3000 |
45
- | Arte de persona (MBTI) | 450 |
46
- | Repertório, listar/editar seus artigos, ver saldo | grátis |
47
-
48
- O Claude avisa o custo antes de gastar, e geração que falha é estornada. Publicar no blog editorial, Coluna Sapiens e o pipeline são exclusivos do dono da plataforma.
49
-
50
- ## Gerar com a sua chave (0 Sinapse)
51
-
52
- Tem crédito na fal, na Kie, na WaveSpeed ou no Replicate? Ponha a chave no `env` do servidor, no config do seu cliente (só as que você tem), e a `sapiens_pontes` gera no provedor com ela e traz a peça pro seu acervo. Você paga o preço do provedor; a casa cobra zero em cima.
53
-
54
- ```json
55
- {
56
- "mcpServers": {
57
- "sapiens": {
58
- "command": "npx",
59
- "args": ["-y", "sapiens-mcp"],
60
- "env": {
61
- "FAL_KEY": "cole-aqui-a-chave-da-fal",
62
- "KIE_API_KEY": "cole-aqui-a-chave-da-kie",
63
- "WAVESPEED_API_KEY": "cole-aqui-a-chave-da-wavespeed",
64
- "REPLICATE_API_TOKEN": "cole-aqui-o-token-do-replicate"
65
- }
66
- }
67
- }
68
- }
69
- ```
70
-
71
- No Claude Code: `claude mcp add sapiens --env FAL_KEY=sua-chave -- npx -y sapiens-mcp` (um `--env` por chave). Depois é só pedir: "gera um retrato da Vera na fal com a minha chave".
72
-
73
- No Claude Desktop tem um caminho sem config: a extensão [sapiens.mcpb](https://sapiensinteticos.b-cdn.net/mcpb/sapiens.mcpb). Abra o arquivo e o Claude pede cada chave num campo próprio, que fica guardado no cofre do sistema. Versão nova é baixar o arquivo de novo e abrir.
74
-
75
- A chave fica na sua máquina e sai dela só pro provedor: não vai em argumento, não volta em resultado, não passa pelo servidor do Sapiens. Por isso a `sapiens_pontes` existe só aqui, no servidor instalado; o conector remoto (claude.ai, ChatGPT) não recebe chave. Os jobs ficam em `~/.sapiens-mcp/pontes-jobs.json`, e o mesmo pedido repetido em 30 minutos devolve o job que já existe em vez de cobrar de novo.
76
-
77
- ## Troubleshooting
78
-
79
- - **"sessionToken expirado" / "Conta Sapiens não conectada".** A chave venceu (90 dias sem uso) ou nunca foi salva. Abra [sapiensinteticos.com/conectar-claude](https://www.sapiensinteticos.com/conectar-claude) logado, gere um código novo e rode `sapiens_meta action=login code=XXXX-XXXX`.
80
- - **Ferramenta some ou some capacidade nova depois de atualizar.** O client roda via `npx -y sapiens-mcp` (sem versão fixa) e pode ter ficado preso num cache antigo. Confira o que está rodando com `sapiens_meta action=version` (mostra a versão do binário + a última do npm + `upToDate`). Se `upToDate:false`, limpe o cache do npx e reinicie o client.
81
- - **"Argumentos inválidos".** A mensagem já diz qual campo faltou ou saiu errado; refaça a chamada com o que ela pede. Não repita a mesma chamada que falhou (3 falhas seguidas fazem o client marcar o servidor como indisponível por ~1 min, um disjuntor anti-loop).
82
- - **Saldo baixo antes de gerar.** `sapiens_meta action=credits` (ou `action=subscription` pro detalhe por bucket) mostra quanto sobra antes de gastar em imagem/música/vídeo.
83
-
84
- ## Privacidade
85
-
86
- O servidor conversa com o backend público do Sapiens (Convex) e, só quando você gera com a sua chave, com o provedor dela (fal, Kie, WaveSpeed ou Replicate), direto da sua máquina. Sua identidade vem sempre do token de login, nunca de parâmetros soltos. Cada conta só mexe no que é dela.
87
-
88
- ## Sobre o Sapiens Sintéticos
89
-
90
- O Sapiens Sintéticos é um Studio de IA que constrói Studios: modelo de pensamento na Era Sintética, método e ferramenta na mão, pra cada pessoa montar o próprio sistema de criação, com voz e régua próprias. O método da casa é o **Método Sapiens**: um caminho pra tirar ideia do papel que começa pelo que te move, e não pela brecha de mercado.
91
-
92
- Em inglês o projeto assina **Synthetic Sapiens**: an AI studio that builds studios. The missing skill is not using AI, it is inventing the system the AI feeds. That system is a studio of your own, the personal computer of the Synthetic Era.
93
-
94
- Site oficial: [sapiensinteticos.com](https://www.sapiensinteticos.com) · Manifesto: [/manifesto](https://www.sapiensinteticos.com/manifesto) · Contexto pra máquina: [/llms.txt](https://www.sapiensinteticos.com/llms.txt)
95
-
96
- ---
97
-
98
- Feito por [BorderLess](https://sapiensinteticos.com). Licença MIT.
1
+ # sapiens-mcp
2
+
3
+ ![Helen Ailith numa mesa escura, antebracos vestidos por um exoesqueleto de luz verde, movendo placas de luz com as maos abertas](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430717302_e8p3i4.webp)
4
+
5
+ <p align="center">
6
+ <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-pt-3e96346c.mp4"><img src="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-pt-497bacfa.jpg" width="720" alt="O sapiens-mcp em 15 segundos: você fala, sua IA executa"></a><br>
7
+ <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-pt-3e96346c.mp4"><b>Assiste em 15 segundos, com som</b></a> · <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-9x16-pt-b5b02542.mp4">corte vertical</a> · <a href="https://sapiensinteticos.b-cdn.net/videos/films/abre-alas/sapiens-mcp-intro-en-e17d0673.mp4">in English</a>
8
+ </p>
9
+
10
+ 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**.
11
+
12
+ ## Pré-requisito
13
+
14
+ Uma conta no Sapiens Sintéticos. Conta e login acontecem só no site (Google ou email), nunca por aqui. Não tem conta? Crie em [sapiensinteticos.com](https://sapiensinteticos.com).
15
+
16
+ ## Instalar (Claude Code)
17
+
18
+ ```bash
19
+ claude mcp add sapiens -- npx -y sapiens-mcp
20
+ ```
21
+
22
+ Precisa de Node 18+. A URL do backend já vem embutida; não precisa configurar nada.
23
+
24
+ ## Conectar sua conta
25
+
26
+ ![Helen segurando uma chave de luz verde entre duas portas: um retangulo de luz sem moldura e um terminal fisico](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430826064_avwjib.webp)
27
+
28
+ 1. Abra **[sapiensinteticos.com/conectar-claude](https://sapiensinteticos.com/conectar-claude)** logado e gere o código (`XXXX-XXXX`, vale 5 min).
29
+ 2. No Claude Code, peça pra logar (ele chama a ferramenta de login), ou rode direto:
30
+
31
+ `sapiens_meta` com `action: "login"` e `code: "XXXX-XXXX"`
32
+
33
+ O token vale 90 dias e renova sozinha a cada uso; fica salvo em `~/.sapiens-mcp/session.json`. Pra sair: `sapiens_meta action=logout`, que revoga a chave no servidor e apaga o arquivo local.
34
+
35
+ ## O que dá pra pedir (e o custo em Sinapses)
36
+
37
+ ![Helen com um braco estendido, orbitada por uma lente, rolos de filme, um vinil, um caderno aberto e um microfone de estudio](https://sapiensinteticos.b-cdn.net/borderlessprotocol/2026/07/1785430870745_waml5a.webp)
38
+
39
+ | O que | Custo |
40
+ |---|---|
41
+ | Gerar imagem (vai pra sua galeria) | ~400-500 |
42
+ | Escrever artigo na voz Sapiens (no seu perfil) | 400 |
43
+ | Voz / narração (Helen TTS) | 500 |
44
+ | Letra de música / render (Musicator) | 300 / 3000 |
45
+ | Arte de persona (MBTI) | 450 |
46
+ | Repertório, listar/editar seus artigos, ver saldo | grátis |
47
+
48
+ O Claude avisa o custo antes de gastar, e geração que falha é estornada. Publicar no blog editorial, Coluna Sapiens e o pipeline são exclusivos do dono da plataforma.
49
+
50
+ ## Gerar com a sua chave (0 Sinapse)
51
+
52
+ Tem crédito na fal, na Kie, na WaveSpeed ou no Replicate? Ponha a chave no `env` do servidor, no config do seu cliente (só as que você tem), e a `sapiens_pontes` gera no provedor com ela e traz a peça pro seu acervo. Você paga o preço do provedor; a casa cobra zero em cima.
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "sapiens": {
58
+ "command": "npx",
59
+ "args": ["-y", "sapiens-mcp"],
60
+ "env": {
61
+ "FAL_KEY": "cole-aqui-a-chave-da-fal",
62
+ "KIE_API_KEY": "cole-aqui-a-chave-da-kie",
63
+ "WAVESPEED_API_KEY": "cole-aqui-a-chave-da-wavespeed",
64
+ "REPLICATE_API_TOKEN": "cole-aqui-o-token-do-replicate"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ No Claude Code: `claude mcp add sapiens --env FAL_KEY=sua-chave -- npx -y sapiens-mcp` (um `--env` por chave). Depois é só pedir: "gera um retrato da Vera na fal com a minha chave".
72
+
73
+ No Claude Desktop tem um caminho sem config: a extensão [sapiens.mcpb](https://sapiensinteticos.b-cdn.net/mcpb/sapiens.mcpb). Abra o arquivo e o Claude pede cada chave num campo próprio, que fica guardado no cofre do sistema. Versão nova é baixar o arquivo de novo e abrir.
74
+
75
+ A chave fica na sua máquina e sai dela só pro provedor: não vai em argumento, não volta em resultado, não passa pelo servidor do Sapiens. Por isso a `sapiens_pontes` existe só aqui, no servidor instalado; o conector remoto (claude.ai, ChatGPT) não recebe chave. Os jobs ficam em `~/.sapiens-mcp/pontes-jobs.json`, e o mesmo pedido repetido em 30 minutos devolve o job que já existe em vez de cobrar de novo.
76
+
77
+ ## Troubleshooting
78
+
79
+ - **"sessionToken expirado" / "Conta Sapiens não conectada".** A chave venceu (90 dias sem uso) ou nunca foi salva. Abra [sapiensinteticos.com/conectar-claude](https://www.sapiensinteticos.com/conectar-claude) logado, gere um código novo e rode `sapiens_meta action=login code=XXXX-XXXX`.
80
+ - **Ferramenta some ou some capacidade nova depois de atualizar.** O client roda via `npx -y sapiens-mcp` (sem versão fixa) e pode ter ficado preso num cache antigo. Confira o que está rodando com `sapiens_meta action=version` (mostra a versão do binário + a última do npm + `upToDate`). Se `upToDate:false`, limpe o cache do npx e reinicie o client.
81
+ - **"Argumentos inválidos".** A mensagem já diz qual campo faltou ou saiu errado; refaça a chamada com o que ela pede. Não repita a mesma chamada que falhou (3 falhas seguidas fazem o client marcar o servidor como indisponível por ~1 min, um disjuntor anti-loop).
82
+ - **Saldo baixo antes de gerar.** `sapiens_meta action=credits` (ou `action=subscription` pro detalhe por bucket) mostra quanto sobra antes de gastar em imagem/música/vídeo.
83
+
84
+ ## Privacidade
85
+
86
+ O servidor conversa com o backend público do Sapiens (Convex) e, só quando você gera com a sua chave, com o provedor dela (fal, Kie, WaveSpeed ou Replicate), direto da sua máquina. Sua identidade vem sempre do token de login, nunca de parâmetros soltos. Cada conta só mexe no que é dela.
87
+
88
+ ## Sobre o Sapiens Sintéticos
89
+
90
+ O Sapiens Sintéticos é um Studio de IA que constrói Studios: modelo de pensamento na Era Sintética, método e ferramenta na mão, pra cada pessoa montar o próprio sistema de criação, com voz e régua próprias. O método da casa é o **Método Sapiens**: um caminho pra tirar ideia do papel que começa pelo que te move, e não pela brecha de mercado.
91
+
92
+ Em inglês o projeto assina **Synthetic Sapiens**: an AI studio that builds studios. The missing skill is not using AI, it is inventing the system the AI feeds. That system is a studio of your own, the personal computer of the Synthetic Era.
93
+
94
+ Site oficial: [sapiensinteticos.com](https://www.sapiensinteticos.com) · Manifesto: [/manifesto](https://www.sapiensinteticos.com/manifesto) · Contexto pra máquina: [/llms.txt](https://www.sapiensinteticos.com/llms.txt)
95
+
96
+ ---
97
+
98
+ Feito por [BorderLess](https://sapiensinteticos.com). Licença MIT.
package/dist/registry.js CHANGED
@@ -218,7 +218,7 @@ export const TOOLS = {
218
218
  handler: trilhas,
219
219
  },
220
220
  sapiens_distribution: {
221
- 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), 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.",
221
+ 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.",
222
222
  schema: distributionSchema,
223
223
  handler: distribution,
224
224
  },
@@ -248,27 +248,27 @@ export const TOOLS = {
248
248
  // É a "skill que anda junto com o pacote": escrevo uma vez, vale pra todos os clients,
249
249
  // sem instalar nada. Cobre os tropeços reais (fluxo do musicator, model no vídeo, o
250
250
  // disjuntor anti-loop). Mantém curto de propósito: viaja em todo handshake.
251
- 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.
252
-
253
- AS SKILLS DA CASA (leia ANTES de operar, não improvise o fluxo):
254
- 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.
255
- - sapiens_skill action=list -> o índice (slug + quando usar cada uma).
256
- - sapiens_skill action=get name=<slug> -> a skill inteira.
257
- Slugs: ${skillMenuLine()}
258
- 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.
259
- Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
260
-
261
- REGRA DE OURO:
262
- - 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.
263
- - 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.
264
- - 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.
265
- - DUAS PORTAS, lado a lado: Sinapse (sapiens_image, sapiens_video: nada pra configurar) ou a CHAVE DA PESSOA num provedor (fal, Kie, WaveSpeed, Replicate, Sogni, a própria placa), que custa zero aqui. No MCP instalado, sapiens_pontes gera com a chave que mora na máquina dela (fal, Kie, WaveSpeed, Replicate) e traz a peça pro acervo; no remoto, ela gera no harness dela e traz por sapiens_gallery action=ingest. sapiens_meta action=pontes diz o que ela já tem configurado: se tiver chave, ofereça as duas portas antes de gastar Sinapse. A chave NUNCA passa pelo Sapiens (nem em arg, nem no chat). Skill 'pontes'.
266
- - REGRA DO TIMEOUT: geração SÍNCRONA 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 (a tabela de onde conferir está na skill 'primeiros-passos').
267
- - "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
268
- - 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.
269
-
270
- 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. Se vier 'characterOffer', siga o que ele manda: são personagens que o usuário escreveu e que podem entrar em cena, e quem OFERECE é você, na hora em que a conversa encostar num deles. Só personagem de autoria dele; a alma de personagem alheio não sai por aqui.
271
-
251
+ 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.
252
+
253
+ AS SKILLS DA CASA (leia ANTES de operar, não improvise o fluxo):
254
+ 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.
255
+ - sapiens_skill action=list -> o índice (slug + quando usar cada uma).
256
+ - sapiens_skill action=get name=<slug> -> a skill inteira.
257
+ Slugs: ${skillMenuLine()}
258
+ 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.
259
+ Cliente que lê resources MCP acha o MESMO conteúdo em skill://sapiens/<slug>/SKILL.md.
260
+
261
+ REGRA DE OURO:
262
+ - 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.
263
+ - 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.
264
+ - 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.
265
+ - DUAS PORTAS, lado a lado: Sinapse (sapiens_image, sapiens_video: nada pra configurar) ou a CHAVE DA PESSOA num provedor (fal, Kie, WaveSpeed, Replicate, Sogni, a própria placa), que custa zero aqui. No MCP instalado, sapiens_pontes gera com a chave que mora na máquina dela (fal, Kie, WaveSpeed, Replicate) e traz a peça pro acervo; no remoto, ela gera no harness dela e traz por sapiens_gallery action=ingest. sapiens_meta action=pontes diz o que ela já tem configurado: se tiver chave, ofereça as duas portas antes de gastar Sinapse. A chave NUNCA passa pelo Sapiens (nem em arg, nem no chat). Skill 'pontes'.
266
+ - REGRA DO TIMEOUT: geração SÍNCRONA 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 (a tabela de onde conferir está na skill 'primeiros-passos').
267
+ - "sessionToken expirado" = refaça login: sapiens_meta action=login com o código de sapiensinteticos.com/conectar-claude.
268
+ - 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.
269
+
270
+ 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. Se vier 'characterOffer', siga o que ele manda: são personagens que o usuário escreveu e que podem entrar em cena, e quem OFERECE é você, na hora em que a conversa encostar num deles. Só personagem de autoria dele; a alma de personagem alheio não sai por aqui.
271
+
272
272
  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.`;
273
273
  // Annotations MCP: título humano + dica read-only. São HINTS (não-confiáveis por
274
274
  // spec): quem gateia de verdade continua o servidor (saldo, gate de admin,
@@ -36,111 +36,111 @@ export function generateCoverLetterHTML(cover, resume, options = {}) {
36
36
  const paragraphs = (cover.paragraphs || [])
37
37
  .map((p) => `<p class="body-p">${escapeHtml(p)}</p>`)
38
38
  .join("\n");
39
- return `<!DOCTYPE html>
40
- <html lang="${lang === "en" ? "en" : "pt-BR"}">
41
- <head>
42
- <meta charset="UTF-8">
43
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
44
- <title>${escapeHtml(resume.name)} · ${title}</title>
45
- <link rel="preconnect" href="https://fonts.googleapis.com">
46
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
47
- <link href="https://fonts.googleapis.com/css2?${spec.fontsQuery}&display=swap" rel="stylesheet">
48
- <style>
49
- *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
50
- html { font-size: 15.5px; -webkit-font-smoothing: antialiased; }
51
- body {
52
- font-family: ${spec.bodyFamily};
53
- color: #1B1E23;
54
- background: ${spec.pageBg};
55
- line-height: 1.6;
56
- padding: 2.2rem 1rem;
57
- }
58
- .sheet {
59
- max-width: 760px;
60
- margin: 0 auto;
61
- background: #fff;
62
- border-radius: 12px;
63
- overflow: hidden;
64
- box-shadow: 0 1px 2px rgba(20,22,27,.05), 0 14px 44px rgba(20,22,27,.09);
65
- padding: 3rem 3.2rem 2.6rem;
66
- position: relative;
67
- }
68
- .sheet::before {
69
- content: '';
70
- position: absolute;
71
- top: 0; left: 0; right: 0;
72
- height: 3px;
73
- background: ${spec.accent};
74
- }
75
- .head { border-bottom: 1px solid #E5E6E8; padding-bottom: 1.2rem; margin-bottom: 1.6rem; }
76
- .head-name {
77
- font-family: ${spec.displayFamily};
78
- font-size: 1.7rem;
79
- font-weight: 700;
80
- letter-spacing: -0.02em;
81
- margin-bottom: .15rem;
82
- }
83
- .head-title {
84
- font-size: .78rem;
85
- font-weight: 700;
86
- text-transform: uppercase;
87
- letter-spacing: .14em;
88
- color: ${spec.accent};
89
- margin-bottom: .55rem;
90
- }
91
- .head-contact { font-size: .76rem; color: #82868E; }
92
- .doc-label {
93
- font-size: .68rem;
94
- font-weight: 700;
95
- text-transform: uppercase;
96
- letter-spacing: .16em;
97
- color: #82868E;
98
- display: flex;
99
- align-items: center;
100
- gap: .65rem;
101
- margin-bottom: 1.2rem;
102
- }
103
- .doc-label::before { content: ''; width: 22px; height: 3px; border-radius: 2px; background: ${spec.accent}; }
104
- .date-line { font-size: .8rem; color: #82868E; margin-bottom: 1.4rem; }
105
- .subject { font-size: .92rem; font-weight: 700; margin-bottom: 1.2rem; }
106
- .greeting { font-size: .9rem; margin-bottom: 1rem; }
107
- .body-p { font-size: .88rem; color: #4A4E57; line-height: 1.8; margin-bottom: 1rem; text-align: justify; }
108
- .closing { margin-top: 1.6rem; font-size: .9rem; }
109
- .signature {
110
- margin-top: .4rem;
111
- font-family: ${spec.displayFamily};
112
- font-size: 1.05rem;
113
- font-weight: 700;
114
- }
115
- @media (max-width: 640px) {
116
- body { padding: .5rem; }
117
- .sheet { border-radius: 0; padding: 1.8rem 1.4rem; }
118
- }
119
- @media print {
120
- @page { margin: 0.7in; size: A4; }
121
- html { font-size: 13px; }
122
- body { padding: 0; background: white; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
123
- .sheet { max-width: 100%; border-radius: 0; box-shadow: none; padding: 0; }
124
- .sheet::before { display: none; }
125
- .body-p { break-inside: avoid; }
126
- }
127
- </style>
128
- </head>
129
- <body>
130
- <div class="sheet">
131
- <header class="head">
132
- <h1 class="head-name">${escapeHtml(resume.name)}</h1>
133
- ${resume.title ? `<p class="head-title">${escapeHtml(resume.title)}</p>` : ""}
134
- <p class="head-contact">${contactLine(resume)}</p>
135
- </header>
136
- <p class="doc-label">${title}</p>
137
- ${cover.dateLine ? `<p class="date-line">${escapeHtml(cover.dateLine)}</p>` : ""}
138
- ${cover.subject ? `<p class="subject">${escapeHtml(cover.subject)}</p>` : ""}
139
- <p class="greeting">${escapeHtml(cover.greeting)}</p>
140
- ${paragraphs}
141
- <p class="closing">${escapeHtml(cover.closing)}</p>
142
- <p class="signature">${escapeHtml(resume.name)}</p>
143
- </div>
144
- </body>
39
+ return `<!DOCTYPE html>
40
+ <html lang="${lang === "en" ? "en" : "pt-BR"}">
41
+ <head>
42
+ <meta charset="UTF-8">
43
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
44
+ <title>${escapeHtml(resume.name)} · ${title}</title>
45
+ <link rel="preconnect" href="https://fonts.googleapis.com">
46
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
47
+ <link href="https://fonts.googleapis.com/css2?${spec.fontsQuery}&display=swap" rel="stylesheet">
48
+ <style>
49
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
50
+ html { font-size: 15.5px; -webkit-font-smoothing: antialiased; }
51
+ body {
52
+ font-family: ${spec.bodyFamily};
53
+ color: #1B1E23;
54
+ background: ${spec.pageBg};
55
+ line-height: 1.6;
56
+ padding: 2.2rem 1rem;
57
+ }
58
+ .sheet {
59
+ max-width: 760px;
60
+ margin: 0 auto;
61
+ background: #fff;
62
+ border-radius: 12px;
63
+ overflow: hidden;
64
+ box-shadow: 0 1px 2px rgba(20,22,27,.05), 0 14px 44px rgba(20,22,27,.09);
65
+ padding: 3rem 3.2rem 2.6rem;
66
+ position: relative;
67
+ }
68
+ .sheet::before {
69
+ content: '';
70
+ position: absolute;
71
+ top: 0; left: 0; right: 0;
72
+ height: 3px;
73
+ background: ${spec.accent};
74
+ }
75
+ .head { border-bottom: 1px solid #E5E6E8; padding-bottom: 1.2rem; margin-bottom: 1.6rem; }
76
+ .head-name {
77
+ font-family: ${spec.displayFamily};
78
+ font-size: 1.7rem;
79
+ font-weight: 700;
80
+ letter-spacing: -0.02em;
81
+ margin-bottom: .15rem;
82
+ }
83
+ .head-title {
84
+ font-size: .78rem;
85
+ font-weight: 700;
86
+ text-transform: uppercase;
87
+ letter-spacing: .14em;
88
+ color: ${spec.accent};
89
+ margin-bottom: .55rem;
90
+ }
91
+ .head-contact { font-size: .76rem; color: #82868E; }
92
+ .doc-label {
93
+ font-size: .68rem;
94
+ font-weight: 700;
95
+ text-transform: uppercase;
96
+ letter-spacing: .16em;
97
+ color: #82868E;
98
+ display: flex;
99
+ align-items: center;
100
+ gap: .65rem;
101
+ margin-bottom: 1.2rem;
102
+ }
103
+ .doc-label::before { content: ''; width: 22px; height: 3px; border-radius: 2px; background: ${spec.accent}; }
104
+ .date-line { font-size: .8rem; color: #82868E; margin-bottom: 1.4rem; }
105
+ .subject { font-size: .92rem; font-weight: 700; margin-bottom: 1.2rem; }
106
+ .greeting { font-size: .9rem; margin-bottom: 1rem; }
107
+ .body-p { font-size: .88rem; color: #4A4E57; line-height: 1.8; margin-bottom: 1rem; text-align: justify; }
108
+ .closing { margin-top: 1.6rem; font-size: .9rem; }
109
+ .signature {
110
+ margin-top: .4rem;
111
+ font-family: ${spec.displayFamily};
112
+ font-size: 1.05rem;
113
+ font-weight: 700;
114
+ }
115
+ @media (max-width: 640px) {
116
+ body { padding: .5rem; }
117
+ .sheet { border-radius: 0; padding: 1.8rem 1.4rem; }
118
+ }
119
+ @media print {
120
+ @page { margin: 0.7in; size: A4; }
121
+ html { font-size: 13px; }
122
+ body { padding: 0; background: white; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
123
+ .sheet { max-width: 100%; border-radius: 0; box-shadow: none; padding: 0; }
124
+ .sheet::before { display: none; }
125
+ .body-p { break-inside: avoid; }
126
+ }
127
+ </style>
128
+ </head>
129
+ <body>
130
+ <div class="sheet">
131
+ <header class="head">
132
+ <h1 class="head-name">${escapeHtml(resume.name)}</h1>
133
+ ${resume.title ? `<p class="head-title">${escapeHtml(resume.title)}</p>` : ""}
134
+ <p class="head-contact">${contactLine(resume)}</p>
135
+ </header>
136
+ <p class="doc-label">${title}</p>
137
+ ${cover.dateLine ? `<p class="date-line">${escapeHtml(cover.dateLine)}</p>` : ""}
138
+ ${cover.subject ? `<p class="subject">${escapeHtml(cover.subject)}</p>` : ""}
139
+ <p class="greeting">${escapeHtml(cover.greeting)}</p>
140
+ ${paragraphs}
141
+ <p class="closing">${escapeHtml(cover.closing)}</p>
142
+ <p class="signature">${escapeHtml(resume.name)}</p>
143
+ </div>
144
+ </body>
145
145
  </html>`;
146
146
  }