primocode 10.11.1 → 10.13.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.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * voicestudio.js — a voz local do VoiceStudio, a primeira da cadeia.
3
+ *
4
+ * "achei isso: https://voicestudio.sh/ podemos usar pra narração do motion,
5
+ * ele entra primeiro na cadeia e depois, se falhar, o Gemini" (Joel, 15/10)
6
+ *
7
+ * O VoiceStudio é um app de desktop (AGPL-3.0) que a pessoa instala e deixa
8
+ * aberto; ele serve uma API no formato da OpenAI em localhost:3900, sem chave
9
+ * no loopback (docs/api-auth.md do repo dele). O PrimoCode só CHAMA a API —
10
+ * não copia nem distribui o código dele, então a licença não pega.
11
+ *
12
+ * Fechado, a porta recusa na hora e a cadeia segue para o Gemini sem esperar.
13
+ * Puro de rede: http do Node, sem dependência.
14
+ *
15
+ * VOICESTUDIO_URL outro endereço (padrão http://127.0.0.1:3900)
16
+ * VOICESTUDIO_VOZ o perfil de voz do app (padrão "default")
17
+ * VOICESTUDIO_MODELO o motor (padrão "omnivoice")
18
+ */
19
+
20
+ 'use strict';
21
+
22
+ const http = require('http');
23
+ const https = require('https');
24
+
25
+ const URL_PADRAO = 'http://127.0.0.1:3900';
26
+ const LIMITE = 4096; // o input máximo do /v1/audio/speech
27
+
28
+ function base(url) { return String(url || process.env.VOICESTUDIO_URL || URL_PADRAO).replace(/\/+$/, ''); }
29
+
30
+ function pedir(metodo, url, { corpo = null, timeout = 800 } = {}) {
31
+ return new Promise((ok) => {
32
+ let u;
33
+ try { u = new URL(url); } catch { ok({ ok: false, error: 'endereço inválido' }); return; }
34
+ const lib = u.protocol === 'https:' ? https : http;
35
+ const dados = corpo ? Buffer.from(JSON.stringify(corpo)) : null;
36
+ const req = lib.request(u, {
37
+ method: metodo,
38
+ headers: dados ? { 'content-type': 'application/json', 'content-length': dados.length } : {},
39
+ }, (res) => {
40
+ const partes = [];
41
+ res.on('data', (c) => partes.push(c));
42
+ res.on('end', () => ok({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, corpo: Buffer.concat(partes) }));
43
+ });
44
+ req.setTimeout(timeout, () => req.destroy(new Error('tempo esgotado')));
45
+ req.on('error', (e) => ok({ ok: false, error: e.message, recusou: /ECONNREFUSED|EADDRNOTAVAIL|ENOTFOUND/.test(e.code || e.message) }));
46
+ if (dados) req.write(dados);
47
+ req.end();
48
+ });
49
+ }
50
+
51
+ /** O app está aberto? Pergunta rápida: fechado, a porta recusa na hora. */
52
+ async function disponivel({ url } = {}) {
53
+ const r = await pedir('GET', base(url) + '/v1/audio/voices', { timeout: 800 });
54
+ return r.ok === true;
55
+ }
56
+
57
+ /**
58
+ * Um texto em áudio (mp3). Nunca lança: devolve {ok, audio} ou {ok:false, error}.
59
+ * O primeiro pedido carrega o modelo no app, por isso o tempo é longo.
60
+ */
61
+ async function falar(texto, { url, voz, modelo, idioma = 'pt', timeout = 10 * 60000 } = {}) {
62
+ const input = String(texto || '').trim();
63
+ if (!input) return { ok: false, error: 'voicestudio: texto vazio' };
64
+ if (input.length > LIMITE) return { ok: false, error: `voicestudio: o texto passa de ${LIMITE} caracteres` };
65
+ if (!(await disponivel({ url }))) return { ok: false, indisponivel: true, error: 'voicestudio: o app não está aberto' };
66
+ const r = await pedir('POST', base(url) + '/v1/audio/speech', {
67
+ corpo: {
68
+ model: modelo || process.env.VOICESTUDIO_MODELO || 'omnivoice',
69
+ input,
70
+ voice: voz || process.env.VOICESTUDIO_VOZ || 'default',
71
+ response_format: 'mp3',
72
+ language: idioma,
73
+ },
74
+ timeout,
75
+ });
76
+ if (!r.ok) return { ok: false, error: `voicestudio: ${r.error || 'status ' + r.status}${r.corpo && r.corpo.length < 400 ? ' ' + r.corpo.toString() : ''}` };
77
+ if (!r.corpo || r.corpo.length < 8) return { ok: false, error: 'voicestudio: áudio vazio' };
78
+ return { ok: true, audio: r.corpo };
79
+ }
80
+
81
+ module.exports = { disponivel, falar, URL_PADRAO, LIMITE };
package/lib/video/auto.js CHANGED
@@ -254,9 +254,22 @@ function juntarSilencios(sil) {
254
254
  return out;
255
255
  }
256
256
 
257
+ /* O que vem depois do auto. Sem pedido falado (15/10: um vídeo do Instagram,
258
+ "edita pra mim?"), o plano é vazio e só a legenda entra — e o agente
259
+ exportava assim. Aí a edição volta para ele decidir, com o cardápio. */
260
+ function depois(passos) {
261
+ if ((passos || []).length) return { semPedido: false, texto: 'inspect para OLHAR os momentos; export renderiza (o 3D sai no export)' };
262
+ return {
263
+ semPedido: true,
264
+ texto: 'A gravação não tem pedido falado: só a legenda entrou, e isso ainda não é a edição. '
265
+ + 'Se a pessoa pediu para editar, a edição é sua: leia a fala (get_transcript) e decida — por exemplo cortar os silêncios (remove_silence), '
266
+ + 'zoom nos momentos fortes (auto_zoom), apoio quando ela cita uma coisa (add_clips), cor (grade). Olhe com inspect antes do export.',
267
+ };
268
+ }
269
+
257
270
  const OPS = {
258
271
  auto: {
259
- uso: 'auto {asset?,dryRun?} — O VÍDEO É O PEDIDO: lê o que a pessoa FALA na gravação ("separe em camadas", "coloca o logo do Claude aqui", "com o estalar de dedos me coloca numa praia", "junta e vira um cérebro", "cortou o silêncio", "o vídeo acaba") e aplica cada efeito no momento em que ela diz, ancorado na palavra, na mão, no estalo e no juntar das mãos; legenda sempre. Use logo depois do import quando pedirem "edite esse vídeo" sem dizer o quê',
272
+ uso: 'auto {asset?,dryRun?} — O VÍDEO É O PEDIDO: lê o que a pessoa FALA na gravação ("separe em camadas", "coloca o logo do Claude aqui", "com o estalar de dedos me coloca numa praia", "junta e vira um cérebro", "cortou o silêncio", "o vídeo acaba") e aplica cada efeito no momento em que ela diz, ancorado na palavra, na mão, no estalo e no juntar das mãos; legenda sempre. Serve quando a gravação TRAZ pedidos falados. Sem pedido falado (vídeo de outra pessoa, ou só fala), ele põe só a legenda, e o resto da edição é decisão sua',
260
273
  async run(ctx, a) {
261
274
  const T = require('./timeline.js');
262
275
  const C = require('./camadas.js');
@@ -317,13 +330,15 @@ const OPS = {
317
330
  if (L.position && ids.length) await roda('style_captions', { ids, position: L.position }, desde !== null ? 'em cima da cabeça, desde o momento dito' : 'em cima da cabeça');
318
331
  }
319
332
  const falhou = feitos.filter((f) => !f.ok);
333
+ const prox = depois(plano.passos);
320
334
  return { ok: !falhou.length, feitos, tookS: Math.round((Date.now() - inicio) / 100) / 10,
321
335
  ...(falhou.length ? { error: falhou.map((f) => `${f.op}: ${f.error}`).join('; ') } : {}),
322
- depois: 'inspect para OLHAR os momentos; export renderiza (o 3D sai no export)' };
336
+ ...(prox.semPedido ? { semPedido: true } : {}),
337
+ depois: prox.texto };
323
338
  },
324
339
  },
325
340
  };
326
341
 
327
342
  const EX = { auto: '{}' };
328
343
 
329
- module.exports = { planejar, frases, normal, fundoPedido, juntarSilencios, OPS, EX };
344
+ module.exports = { planejar, frases, normal, fundoPedido, juntarSilencios, depois, OPS, EX };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "10.11.1",
3
+ "version": "10.13.0",
4
4
  "description": "PrimoCode — agente de engenharia com IA e cursor próprio. Requer conta Conecta Primo AI (Premium ou Super). Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
5
5
  "main": "bin/primocode.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node bin/primocode.js",
11
- "test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/skills-de-fora.test.js && node test/skills-novas.test.js && node test/comandos.test.js && node test/estudio.test.js && node test/voz.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/claude-engine.test.js && node test/codex.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/casar.test.js && node test/recado.test.js && node test/releitura.test.js && node test/snapshot.test.js && node test/video.test.js && node test/limpeza.test.js && node test/autonomia.test.js && node test/desfazer.test.js && node test/desfazer-git.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/conta.test.mjs && node test/planos-tela.test.js && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js && node test/contexto.test.js && node test/perigo.test.js && node test/rapidez.test.js && node test/mascotes.test.js && node test/comandos-extra.test.js && node test/offline.test.js && node test/edicao.test.js && node test/composicao.test.js && node test/acervo-site.test.js && node test/impacto.test.js && node test/cinema.test.js && node test/producao.test.js && node test/ende.test.js && node test/capa.test.js && node test/broll.test.js && node test/morfar.test.js && node test/motion.test.js && node test/motion-roteiro.test.js && node test/kit-motion.test.js && node test/gerativo.test.js && node test/gerativo-identidade.test.js && node test/gerativo-moldura.test.js && node test/gerativo-heroi.test.js && node test/gerativo-ambiente.test.js && node test/gerativo-variedade.test.js && node test/hibrido.test.js && node test/batida.test.js && node test/estudio-acervo.test.js && node test/estudio-diretor.test.js && node test/estudio-marca.test.js && node test/estudio-novo-projeto.test.js && node test/estudio-plano.test.js && node test/estudio-slides.test.js && node test/estudio-video.test.js && node test/revisor.test.js && node test/conferencia.test.js && node test/cor.test.js && node test/assinatura.test.js && node test/biblioteca.test.js && node test/mcp.test.js && node test/personas.test.js && node test/efeitos.test.js && node test/auto.test.js && node test/colar.test.js && node test/rolar.test.js && node test/orquestra.test.js && node test/shell-limpo.test.js && node test/editar-remotion.test.js && node test/variacao3d.test.js && node test/portao-skill.test.js && node test/clones.test.js && node test/kokoro.test.js && node test/fluido.test.js && node test/zoom.test.js && node test/personagem.test.js && node test/tela.test.js && node test/margem.test.js && node test/travado.test.js && node test/leitura.test.js && node test/producao-provedores.test.js && node test/broll-frase.test.js && node test/vazamento.test.js && node test/colecao.test.js && node test/mundo.test.js && node test/mcp-procurar.test.js && node test/selecao.test.js && node test/nome-curto.test.js && node test/agents-md.test.js && node test/outra-pasta.test.js && node test/estudio-sem-id.test.js && node test/estudio-objeto-do-site.test.js && node test/cenarios.test.js && node test/movimentos.test.js && node test/gerativo-so-fala.test.js && node test/estilos.test.js && node test/proporcao.test.js && node test/cena.test.js && node test/sem-narracao.test.js"
11
+ "test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/skills-de-fora.test.js && node test/skills-novas.test.js && node test/comandos.test.js && node test/estudio.test.js && node test/voz.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/claude-engine.test.js && node test/codex.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/casar.test.js && node test/recado.test.js && node test/releitura.test.js && node test/snapshot.test.js && node test/video.test.js && node test/limpeza.test.js && node test/autonomia.test.js && node test/desfazer.test.js && node test/desfazer-git.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/conta.test.mjs && node test/planos-tela.test.js && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js && node test/contexto.test.js && node test/perigo.test.js && node test/rapidez.test.js && node test/mascotes.test.js && node test/comandos-extra.test.js && node test/offline.test.js && node test/edicao.test.js && node test/composicao.test.js && node test/acervo-site.test.js && node test/impacto.test.js && node test/cinema.test.js && node test/producao.test.js && node test/ende.test.js && node test/capa.test.js && node test/broll.test.js && node test/morfar.test.js && node test/motion.test.js && node test/motion-roteiro.test.js && node test/kit-motion.test.js && node test/gerativo.test.js && node test/gerativo-identidade.test.js && node test/gerativo-moldura.test.js && node test/gerativo-heroi.test.js && node test/gerativo-ambiente.test.js && node test/gerativo-variedade.test.js && node test/hibrido.test.js && node test/batida.test.js && node test/estudio-acervo.test.js && node test/estudio-diretor.test.js && node test/estudio-marca.test.js && node test/estudio-novo-projeto.test.js && node test/estudio-plano.test.js && node test/estudio-slides.test.js && node test/estudio-video.test.js && node test/revisor.test.js && node test/conferencia.test.js && node test/cor.test.js && node test/assinatura.test.js && node test/biblioteca.test.js && node test/mcp.test.js && node test/personas.test.js && node test/efeitos.test.js && node test/auto.test.js && node test/colar.test.js && node test/rolar.test.js && node test/orquestra.test.js && node test/shell-limpo.test.js && node test/editar-remotion.test.js && node test/variacao3d.test.js && node test/portao-skill.test.js && node test/clones.test.js && node test/kokoro.test.js && node test/fluido.test.js && node test/zoom.test.js && node test/personagem.test.js && node test/tela.test.js && node test/margem.test.js && node test/travado.test.js && node test/leitura.test.js && node test/producao-provedores.test.js && node test/broll-frase.test.js && node test/vazamento.test.js && node test/colecao.test.js && node test/mundo.test.js && node test/mcp-procurar.test.js && node test/selecao.test.js && node test/nome-curto.test.js && node test/agents-md.test.js && node test/outra-pasta.test.js && node test/estudio-sem-id.test.js && node test/estudio-objeto-do-site.test.js && node test/cenarios.test.js && node test/movimentos.test.js && node test/gerativo-so-fala.test.js && node test/estilos.test.js && node test/proporcao.test.js && node test/cena.test.js && node test/sem-narracao.test.js && node test/camadas.test.js && node test/gerativo-referencias.test.js && node test/voicestudio.test.js"
12
12
  },
13
13
  "engines": {
14
14
  "node": ">=18.17.0"
@@ -45,6 +45,12 @@ por 1 a 3 segundos. Depois volta. É isso que dá ritmo a vídeo falado.
45
45
  Busque a imagem pelo que ela DISSE naquele instante (você tem os tempos da
46
46
  transcrição). Volte sempre antes de a frase acabar.
47
47
 
48
+ ## Vídeo em camadas
49
+
50
+ Pediu "edição nesse nível", motion por cima da fala, rosto que vira cartão ou
51
+ bolinha: `acao:"camadas"`. Você decide as batidas; a descrição da ferramenta
52
+ diz o formato.
53
+
48
54
  ## Troca de fundo
49
55
 
50
56
  `acao:"recortar-pessoa"` devolve a pessoa com fundo transparente. Ponha uma
@@ -7,43 +7,35 @@ autor: PrimoCode
7
7
  Não existe template. Cada propriedade deste vídeo é uma decisão sua, e a
8
8
  primeira decisão é **não repetir o vídeo anterior**.
9
9
 
10
- ## O que denuncia vídeo feito por IA
11
-
12
- Não faça nada disto, em nenhum vídeo:
13
-
14
- - gradiente roxo-para-azul, e gradiente de duas
15
- cores que não têm relação com o assunto;
16
- - tudo centralizado, do primeiro ao último quadro;
17
- - uma fonte só, em todos os tamanhos;
18
- - texto que aparece com `opacidade 0→1` e mais nada, cena após cena;
19
- - ícone genérico flutuando ao lado de cada frase;
20
- - tempo igual em todas as cenas;
21
- - transição igual entre todas.
22
-
23
- ## O que separa motion bom de slide animado
24
-
25
- **A câmera existe.** Um zoom lento de 1.0 para 1.08 na cena inteira dá vida a
26
- um quadro parado.
27
-
28
- **Nada entra do nada.** Um elemento entra vindo de algum lugar (de baixo, de
29
- trás, de fora do quadro), com `spring` do Remotion ou easing de saída. Movimento
30
- linear é a assinatura da máquina: no mundo físico nada começa e para na mesma
31
- velocidade.
32
-
33
- **Profundidade.** Parallax (fundo devagar, frente rápido); `perspective` com
34
- `rotateX`/`translateZ`, ou three, para 3D de verdade.
35
-
36
- **Ritmo.** Informação simples 1,5–2,5 s; frase para ler 3–4 s; fecho 2 s.
37
-
38
- **Silêncio visual.** Um quadro com uma frase e mais nada vale mais que um com
39
- seis elementos. Espaço vazio é composição, não desperdício.
10
+ ## O que denuncia IA (nunca)
11
+
12
+ Gradiente roxo-azul ou sem relação com o assunto; tudo centralizado; uma fonte
13
+ só; texto que só faz `opacidade 0→1`; ícone genérico ao lado de cada frase;
14
+ tempo e transição iguais em todas as cenas; letra cortada ou embaralhada na troca.
15
+
16
+ ## A régua (medida nas referências que o Joel aprovou)
17
+
18
+ Números, não estilo pronto: o estilo continua seu.
19
+
20
+ - **Escala:** título de 1 a 3 palavras ocupando 55–85% da largura. Nada abaixo de 2% da altura.
21
+ - **Ritmo:** uma ideia por cena, 1 a 2 s (frase para ler 2–3 s). Com trilha, corte na batida.
22
+ - **Entrada:** máscara (sobe de baixo, recorte reto), palavra por palavra de desfocado
23
+ para nítido (0,15–0,3 s cada) ou esticando com borrão; `spring` ou ataque rápido e
24
+ freio forte. **Saída:** a frase sai inteira (desliza, recua, corte seco).
25
+ - **Cor:** fundo chapado, 1 tinta + 1 acento, contraste de 3:1 ou mais; até 5 cores.
26
+ - **Segunda voz:** o apoio é menor (~4% da altura), na segunda fonte, e chega 0,25–0,5 s depois.
27
+ - **À mão:** na palavra que é a tese, um círculo de caneta que se desenha (com ✓ ou seta).
28
+ - **Acento vivo e câmera:** um ponto ou forma acompanha o título e vira o elemento da
29
+ próxima cena (a transição é o elemento). Zoom lento de 1.0 a 1.08 e parallax dão vida.
30
+ - **Produto:** a interface é código (cartão, toggle, cursor que clica com resultado, número que conta).
31
+ - **Explicar:** uma cena ensina desenhando (curva, gráfico, contorno com âncoras).
32
+ - **Fecho:** a marca sozinha, centralizada, 1,5–2 s. Moldura de ferramenta (cantos,
33
+ timecode) só em reel de marca ou tecnologia: `moldura:"cantos"`.
40
34
 
41
35
  ## O caminho: você escreve cada cena
42
36
 
43
- Motion nesse nível não sai de molde: cada cena é UMA ideia desenhada para a
44
- fala — a interface do produto montando, o número que cresce, o objeto em 3D,
45
- o gráfico que mostra a ideia, a palavra gigante que se transforma. Quem
46
- desenha é você, em React.
37
+ Cada cena é UMA ideia desenhada para a fala (a interface montando, o número
38
+ que cresce, o objeto 3D, a palavra gigante). Quem desenha é você, em React.
47
39
 
48
40
  1. `estudio_projeto` (criar). A marca tem site? `estudio_site` mede cores e fontes.
49
41
  2. `estudio_midia acao:"estilos"`: monte a identidade pelo assunto em
@@ -62,23 +54,13 @@ desenha é você, em React.
62
54
  arquivo e mande o roteiro de novo.
63
55
  6. `estudio_render`. A voz, a trilha, os sons e o fundo são do Estúdio.
64
56
 
65
- **Variedade:** `estudio_midia acao:"efeitos"` antes das cenas; adapte e use
66
- como `componente`. Uma técnica nova por vídeo. Risco em 2+ linhas: traço por linha.
67
-
68
- Parte sem `componente` cai no gerativo (a tipografia anima sozinha): é a
69
- reserva, não o objetivo. Sem imagem é o padrão (foto só se pedirem).
70
-
71
- ## Cor e tipo
72
-
73
- A paleta sai do ASSUNTO, no `estiloVisual` (um nome do cardápio ou 5 cores
74
- suas). Se a marca existe, as cores medidas pelo `estudio_site` mandam — não
75
- as que você imagina que ela usa.
57
+ **Variedade:** `estudio_midia acao:"efeitos"`, adapte e use como `componente`; uma
58
+ técnica nova por vídeo. Parte sem `componente` cai no gerativo (a reserva). Foto só se pedirem.
76
59
 
77
- Duas fontes, no máximo: display para o que grita, neutra para o que se lê.
60
+ ## Cor, tipo e conferência
78
61
 
79
- ## Antes de renderizar
62
+ A paleta sai do ASSUNTO (`estiloVisual`); a marca existe? As cores medidas pelo
63
+ `estudio_site` mandam. Duas fontes: display para o que grita, neutra para o que se lê.
80
64
 
81
- - Os tempos das cenas batem com os tempos MEDIDOS da narração.
82
- - Nenhuma cena tem a mesma composição da anterior.
83
- - Leia o seu próprio plano e pergunte: em que este vídeo é diferente do
84
- último que eu fiz? Se não houver resposta, refaça antes de renderizar.
65
+ Antes do render: os tempos batem com a narração MEDIDA, nenhuma cena repete a
66
+ composição da anterior, e este vídeo é diferente do último. Senão, refaça.