primocode 10.14.0 → 10.14.1

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/lib/act.js CHANGED
@@ -13,6 +13,8 @@
13
13
  const { c, Spinner, PENSANDO } = require('./ui');
14
14
  const fala = require('./fala');
15
15
  const especialistas = require('./especialistas');
16
+ const limites = require('./limites');
17
+ let avisouParalelo = false;
16
18
  const tools = require('./tools');
17
19
  const api = require('./api');
18
20
  const cfg = require('./config');
@@ -1322,8 +1324,12 @@ async function runAct(userPrompt, opts) {
1322
1324
  const spawns = toolCalls.filter((tc) => tc.name === 'spawn_agent');
1323
1325
  const resultadosSpawn = new Map();
1324
1326
  if (spawns.length && !PARAR) garantirPasta();
1325
- if (spawns.length > 1 && !PARAR) {
1326
- const juntos = spawns.slice(0, 8);
1327
+ /* O plano decide quantos subagentes rodam JUNTOS (lib/limites.js; grátis e
1328
+ Primo: um de cada vez, o resto continua em fila). Nunca recusa: só diz o porquê. */
1329
+ const par = spawns.length > 1 ? await limites.paralelo() : null;
1330
+ if (par) { const av = par.aviso(spawns.length); if (av && !avisouParalelo) { avisouParalelo = true; console.log(` ${av}`); } }
1331
+ if (spawns.length > 1 && par.max > 1 && !PARAR) {
1332
+ const juntos = spawns.slice(0, par.max);
1327
1333
  fala.paralelo(juntos.length, profundidade);
1328
1334
  const ids = juntos.map((tc) => anotar(tc, 'permitida'));
1329
1335
  const rs = await Promise.all(juntos.map((tc) =>
@@ -19,7 +19,7 @@ const zlib = require('zlib');
19
19
  const { spawnSync } = require('child_process');
20
20
 
21
21
  const LOCAL = { ok: true, offline: true, liberado: true, plano: 'local', restam: null, usados: 0,
22
- recursos: { videosMes: 0, resolucao: 2160, marcaDagua: false } };
22
+ recursos: { videosMes: 0, resolucao: 2160, marcaDagua: false, narracaoPremium: true, clubeCompleto: true, scriptsAE: true, subagentes: 7, kitsMarca: 0, versoesEditor: 0 } };
23
23
 
24
24
  /** Pergunta (acao "checar") ou conta (acao "gastar") no servidor. */
25
25
  async function consultar(acao = 'checar', { buscar = fetch, servidor, token, prazo = 6000 } = {}) {
@@ -33,6 +33,7 @@ async function consultar(acao = 'checar', { buscar = fetch, servidor, token, pra
33
33
  });
34
34
  const j = await r.json();
35
35
  if (!r.ok || !j || !j.recursos) return LOCAL;
36
+ require('../limites.js').guardar(j);
36
37
  return j;
37
38
  } catch { return LOCAL; }
38
39
  }
@@ -137,14 +138,20 @@ function aplicar(arquivo, recursos, { rodar = spawnSync, dim = dimensoes } = {})
137
138
  async function comCota(render, deps = {}) {
138
139
  const c = await (deps.consultar || consultar)('checar');
139
140
  if (!c.liberado) return { ok: false, error: mensagemLimite(c), cota: { plano: c.plano, restam: 0, upgrade: c.upgrade } };
141
+ const lim = require('../limites.js');
142
+ lim.guardar(c); lim.pegarAvisos();
140
143
  const r = await render();
144
+ const extra = lim.pegarAvisos();
141
145
  if (!r || !r.ok) return r;
142
146
  const a = c.offline ? { ok: true, mudou: false } : (deps.aplicar || aplicar)(r.video, c.recursos);
143
147
  if (!c.offline) await (deps.consultar || consultar)('gastar');
144
148
  const restam = c.restam == null ? null : Math.max(0, c.restam - 1);
145
149
  return { ...r, plano: { id: c.plano, restam, resolucao: c.recursos.resolucao, marcaDagua: !!(a.mudou && c.recursos.marcaDagua) },
146
- ...(c.offline ? { aviso: 'sem rede: modo local, sem cota nem marca d\'água.' }
147
- : restam === 0 ? { aviso: 'Foi o seu último vídeo do mês. Veja os planos com: primocode planos' } : {}) };
150
+ ...((() => {
151
+ const a = [...(c.offline ? ['sem rede: modo local, sem cota nem marca d\'água.'] : []),
152
+ ...(!c.offline && restam === 0 ? ['Foi o seu último vídeo do mês. Veja os planos com: primocode planos'] : []), ...extra];
153
+ return a.length ? { aviso: a.join(' ') } : {};
154
+ })()) };
148
155
  }
149
156
 
150
157
  module.exports = { LOCAL, consultar, mensagemLimite, pngMarca, filtros, aplicar, comCota };
@@ -249,6 +249,11 @@ async function editar(args = {}, { pasta, deps = {} } = {}) {
249
249
  return { ok: true, versao: v.n, video, versoes: lista(), nota: `voltou para a v${v.n}; a próxima mudança vira a v${ix.versoes.reduce((m, x) => Math.max(m, x.n), 0) + 1}.` };
250
250
  }
251
251
 
252
+ // O plano limita as edições guardadas por vídeo (grátis 3). Prévia e a versão original não contam.
253
+ const gastas = indice(pasta).versoes.filter((v) => !v.previa && v.resumo !== 'original').length;
254
+ const lim = await (deps.cabeMais ? deps.cabeMais(gastas) : require('../limites.js').cabeMais('versoesEditor', gastas));
255
+ if (!lim.ok && !args.previa) return { ok: false, error: lim.error + ' Dá para voltar a uma versão (voltar_para) ou ver as prévias.' };
256
+
252
257
  const spec = ler(ARQ(pasta));
253
258
  const mudancas = Array.isArray(args.mudancas) ? args.mudancas : [];
254
259
  if (!mudancas.length && !args.final) return { ok: false, error: 'mudancas está vazio (ou use final:true para renderizar a prévia em qualidade final).' };
@@ -508,7 +508,7 @@ async function ferramentaMidia(args) {
508
508
  if (args.id && !p) return semProjeto(args.id);
509
509
 
510
510
  if (acao === 'projetos') return require('./projetos.js').procurar({ termo: args.termo, categoria: args.categoria, limite: args.limite });
511
- if (acao === 'projeto') return require('./projetos.js').ver(args.nome || args.id_item || args.termo);
511
+ if (acao === 'projeto') return require('./projetos.js').ver(args.nome || args.id_item || args.termo, { c: await require('../limites.js').situacao() });
512
512
 
513
513
  if (acao === 'fontes') {
514
514
  if (args.conferir) return assets.conferirFonte(args.conferir);
@@ -712,8 +712,10 @@ async function ferramentaMotion(args) {
712
712
  if (!args.id || !p) return semProjeto(args.id);
713
713
  let receita = null;
714
714
  if (args.receita) {
715
- const r = require('./projetos.js').ver(args.receita);
715
+ const r = require('./projetos.js').ver(args.receita, { c: await require('../limites.js').situacao() });
716
716
  if (!r.ok) return r;
717
+ // Grátis vê o resumo da receita, não monta a partir dela: diz o porquê em vez de renderizar pela metade.
718
+ if (r.bloqueado) return { ok: false, error: r.aviso, projeto: r.projeto };
717
719
  receita = r.projeto;
718
720
  }
719
721
  return require('./cota.js').comCota(() => renderMotion(args, p, receita));
@@ -1204,6 +1206,11 @@ function vazamentoNoComeco(lista, palavras, direcao = '') {
1204
1206
  }
1205
1207
 
1206
1208
  async function narrarComGemini(lista, mp3, { voz, direcao } = {}) {
1209
+ /* A narração premium é do Pro em diante (lib/limites.js). Sem ela a corrente
1210
+ segue para a voz local (Kokoro/Edge) e o aviso explica o porquê. */
1211
+ const lim = require('../limites.js');
1212
+ const prem = await lim.exigir('narracaoPremium');
1213
+ if (!prem.ok) { lim.avisar(prem.error + ' A narração saiu com a voz local.'); return { ok: false, error: prem.error }; }
1207
1214
  try {
1208
1215
  const cfg = require('../config').loadConfig();
1209
1216
  const api = require('../api');
@@ -47,11 +47,23 @@ function procurar({ termo, categoria, limite = 10 } = {}) {
47
47
  }
48
48
 
49
49
  /** A receita inteira, pelo id (ou, na falta, o melhor palpite do texto). */
50
- function ver(id) {
50
+ function ver(id, { c } = {}) {
51
51
  const q = sem(id).trim();
52
52
  if (!q) return { ok: false, error: 'diga o id da receita (acao:"projetos" lista).' };
53
53
  const p = todos().find((x) => x.id === q) || procurar({ termo: q, limite: 1 }).projetos.map((x) => todos().find((y) => y.id === x.id))[0];
54
54
  if (!p) return { ok: false, error: `não há receita "${id}".`, ids: todos().map((x) => x.id) };
55
+ // O plano decide (lib/limites.js): grátis vê o resumo; sem scriptsAE a receita vem sem os scripts.
56
+ if (c && c.recursos) {
57
+ const lim = require('../limites.js');
58
+ if (!c.recursos.clubeCompleto) {
59
+ const { id: i, titulo, categoria, resumo, indicado, estilo, ficha } = p;
60
+ return { ok: true, bloqueado: true, projeto: { id: i, titulo, categoria, resumo, indicado, estilo, ficha }, aviso: lim.bloqueio('clubeCompleto', c) };
61
+ }
62
+ if (p.scripts_ae && !c.recursos.scriptsAE) {
63
+ const { scripts_ae: _, ...resto } = p;
64
+ return { ok: true, projeto: resto, scripts_ae_bloqueado: lim.bloqueio('scriptsAE', c) };
65
+ }
66
+ }
55
67
  // Os scripts do After Effects ficam em disco: a resposta leva o caminho, e o agente lê só o que precisar.
56
68
  if (p.scripts_ae) p.scripts_ae = { ...p.scripts_ae, caminho: path.join(__dirname, 'dados', 'projetos-ae', p.id.replace(/^clube-/, '')) };
57
69
  return { ok: true, projeto: p };
package/lib/limites.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * limites.js — o que o plano libera, aplicado de verdade na CLI.
3
+ *
4
+ * A tabela mora em server/lib/planos.js; o servidor responde o plano da pessoa
5
+ * em POST /api/video/cota (recursos + o catálogo enxuto). Aqui ficam:
6
+ * - situacao(): o plano agora, guardado 5 min na memória do processo;
7
+ * - o MODO LOCAL generoso (sem rede, servidor mudo ou campo que o servidor
8
+ * ainda não manda): nada é barrado. Errar para o lado de quem trabalha;
9
+ * - bloqueio(): a mensagem curta e honesta — o quê, em que plano você está,
10
+ * qual plano libera e o comando para assinar. Nunca barra sem dizer o porquê.
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const GENEROSO = { videosMes: 0, resolucao: 2160, marcaDagua: false, narracaoPremium: true, clubeCompleto: true,
16
+ scriptsAE: true, prioridade: 0, subagentes: 7, kitsMarca: 0, versoesEditor: 0 };
17
+ const MAX_PARALELO = 8;
18
+ const VALIDADE = 5 * 60000;
19
+
20
+ let cache = null;
21
+ const avisos = [];
22
+
23
+ /** Completa o que o servidor não mandou com o generoso (campo novo, servidor velho). */
24
+ function completar(c) {
25
+ const r = { ...GENEROSO };
26
+ for (const [k, v] of Object.entries((c && c.recursos) || {})) if (v !== undefined && v !== null) r[k] = v;
27
+ return { ...(c || {}), recursos: r };
28
+ }
29
+
30
+ function guardar(c) { cache = { em: Date.now(), c: completar(c) }; }
31
+ function esquecer() { cache = null; }
32
+
33
+ /** Nos testes (node test/x.test.js) ninguém bate no servidor de verdade: vale o modo local. */
34
+ const emTeste = () => /\.test\.m?js$/.test(String(process.argv[1] || '')) || process.env.PRIMOCODE_LIMITES === 'local';
35
+
36
+ /** O plano agora: {plano, recursos, planos, offline}. Nunca lança. */
37
+ async function situacao(deps = {}) {
38
+ if (cache && Date.now() - cache.em < VALIDADE) return cache.c;
39
+ if (emTeste() && !deps.consultar) return completar(require('./estudio/cota.js').LOCAL);
40
+ let c;
41
+ try { c = await (deps.consultar || require('./estudio/cota.js').consultar)('checar'); } catch { c = require('./estudio/cota.js').LOCAL; }
42
+ guardar(c);
43
+ return cache.c;
44
+ }
45
+ const recursos = async (deps) => (await situacao(deps)).recursos;
46
+
47
+ const reais = (n) => `R$ ${Number(n).toFixed(2).replace('.', ',')}`;
48
+
49
+ /* Cada recurso: o que o plano ganha ao passar, e o texto de quando bate. */
50
+ const RECURSOS = {
51
+ narracaoPremium: { de: (r) => !!r.narracaoPremium, texto: 'A narração premium (voz do Gemini)', libera: 'a narração premium' },
52
+ clubeCompleto: { de: (r) => !!r.clubeCompleto, texto: 'A receita completa do Clube (etapas e prompt)', libera: 'as receitas completas do Clube' },
53
+ scriptsAE: { de: (r) => !!r.scriptsAE, texto: 'Os scripts de After Effects modelo', libera: 'os scripts de After Effects modelo' },
54
+ kitsMarca: { n: (r) => r.kitsMarca, texto: (r) => `Você já tem ${r.kitsMarca} kit${r.kitsMarca > 1 ? 's' : ''} de marca, o limite do plano`, libera: (r) => (r.kitsMarca ? `${r.kitsMarca} kits de marca` : 'kits de marca sem teto') },
55
+ versoesEditor: { n: (r) => r.versoesEditor, texto: (r) => `Este vídeo já tem ${r.versoesEditor} edições, o limite do plano`, libera: (r) => (r.versoesEditor ? `${r.versoesEditor} edições por vídeo` : 'edições sem teto') },
56
+ subagentes: { n: (r) => r.subagentes + 1, texto: (r) => `Seu plano roda ${r.subagentes + 1} subagente${r.subagentes ? 's juntos' : ' por vez'}`, libera: (r) => (r.subagentes ? `${r.subagentes + 1} subagentes juntos` : 'subagentes em paralelo') },
57
+ };
58
+
59
+ /** O primeiro plano acima do atual que libera `chave` (ou null). */
60
+ function quemLibera(chave, c) {
61
+ const def = RECURSOS[chave], lista = c.planos || [];
62
+ const i = lista.findIndex((p) => p.id === c.plano);
63
+ return lista.slice(i + 1).find((p) => (def.de ? def.de(p.recursos) : (def.n(p.recursos) === 0 || def.n(p.recursos) > def.n(c.recursos)))) || null;
64
+ }
65
+
66
+ /** A mensagem de limite: curta, com o motivo, o degrau que libera e o comando. */
67
+ function bloqueio(chave, c) {
68
+ const def = RECURSOS[chave], r = c.recursos;
69
+ const nome = ((c.planos || []).find((p) => p.id === c.plano) || {}).nome || c.plano;
70
+ const p = quemLibera(chave, c);
71
+ const texto = typeof def.texto === 'function' ? def.texto(r) : `${def.texto} não vêm no plano ${nome}`;
72
+ const libera = typeof def.libera === 'function' ? def.libera(p ? p.recursos : r) : def.libera;
73
+ return `${texto} (plano ${nome}).`
74
+ + (p ? ` O ${p.nome} (${reais(p.preco)}/mês) libera ${libera}.` : '')
75
+ + ' Veja e assine: primocode planos (ou primocode.web.app/planos).';
76
+ }
77
+
78
+ /** Libera o que é só ligado/desligado. {ok:true} ou {ok:false, error}. */
79
+ async function exigir(chave, deps) {
80
+ const c = await situacao(deps);
81
+ return RECURSOS[chave].de(c.recursos) ? { ok: true } : { ok: false, error: bloqueio(chave, c) };
82
+ }
83
+
84
+ /** Limite numérico: `usados` já é o que existe; passa se o teto é 0 (sem teto) ou se ainda cabe mais um. */
85
+ async function cabeMais(chave, usados, deps) {
86
+ const c = await situacao(deps), teto = RECURSOS[chave].n(c.recursos);
87
+ return !teto || usados < teto ? { ok: true } : { ok: false, error: bloqueio(chave, c) };
88
+ }
89
+
90
+ /** Quantos subagentes rodam juntos na mesma rodada (o principal fica de fora). */
91
+ async function paralelo(deps) {
92
+ const c = await situacao(deps);
93
+ return { max: Math.min(MAX_PARALELO, c.recursos.subagentes + 1), aviso: (n) => (n > c.recursos.subagentes + 1 ? bloqueio('subagentes', c) : null) };
94
+ }
95
+
96
+ /** Avisos que o resultado da ferramenta leva ao fim da vez (ex.: narração premium trocada pela local). */
97
+ function avisar(msg) { if (msg && !avisos.includes(msg)) avisos.push(msg); }
98
+ function pegarAvisos() { return avisos.splice(0); }
99
+
100
+ module.exports = { GENEROSO, MAX_PARALELO, situacao, recursos, guardar, esquecer, completar, bloqueio, quemLibera, exigir, cabeMais, paralelo, avisar, pegarAvisos, RECURSOS };
package/lib/marca-kit.js CHANGED
@@ -280,6 +280,9 @@ async function main(argv, { perguntar, dizer = console.log, pasta = process.cwd(
280
280
  const aqui = resto.includes('aqui');
281
281
  switch (op) {
282
282
  case 'criar': {
283
+ // O plano limita quantos kits se guarda (grátis 1): diz o porquê antes de perguntar qualquer coisa.
284
+ const lim = await (deps && deps.cabeMais ? deps.cabeMais(listar().length) : require('./limites.js').cabeMais('kitsMarca', listar().length));
285
+ if (!lim.ok) { dizer(lim.error); return 1; }
283
286
  const t = perguntar ? null : perguntarNoTerminal();
284
287
  try {
285
288
  const r = await criar({ perguntar: perguntar || t.ask, dizer, deps });
package/lib/planos.js CHANGED
@@ -15,14 +15,14 @@
15
15
  const https = require('https');
16
16
  const http = require('http');
17
17
 
18
- const R = (videosMes, resolucao, marcaDagua, narracaoPremium, clubeCompleto, scriptsAE, prioridade, subagentes) =>
19
- ({ videosMes, resolucao, marcaDagua, narracaoPremium, clubeCompleto, scriptsAE, prioridade, subagentes });
20
- const GRATIS = { id: 'free', nome: 'Grátis', preco: 0, recursos: R(3, 720, true, false, false, false, 0, 0), inclui: [] };
18
+ const R = (videosMes, resolucao, marcaDagua, narracaoPremium, clubeCompleto, scriptsAE, prioridade, subagentes, kitsMarca, versoesEditor) =>
19
+ ({ videosMes, resolucao, marcaDagua, narracaoPremium, clubeCompleto, scriptsAE, prioridade, subagentes, kitsMarca, versoesEditor });
20
+ const GRATIS = { id: 'free', nome: 'Grátis', preco: 0, recursos: R(3, 720, true, false, false, false, 0, 0, 1, 3), inclui: [] };
21
21
  const RESERVA = [
22
- { id: 'primo', nome: 'Primo', preco: 69.9, recursos: R(15, 720, false, false, true, false, 0, 0), inclui: [] },
23
- { id: 'pro', nome: 'Primo Pro', preco: 129.9, recursos: R(60, 1080, false, true, true, true, 1, 1), inclui: [] },
24
- { id: 'max', nome: 'Primo Max', preco: 299.9, recursos: R(200, 2160, false, true, true, true, 2, 3), inclui: [] },
25
- { id: 'studio', nome: 'Primo Studio', preco: 599.9, recursos: R(800, 2160, false, true, true, true, 3, 8), inclui: [] },
22
+ { id: 'primo', nome: 'Primo', preco: 69.9, recursos: R(15, 720, false, false, true, false, 0, 0, 5, 10), inclui: [] },
23
+ { id: 'pro', nome: 'Primo Pro', preco: 129.9, recursos: R(60, 1080, false, true, true, true, 1, 1, 20, 30), inclui: [] },
24
+ { id: 'max', nome: 'Primo Max', preco: 299.9, recursos: R(200, 2160, false, true, true, true, 2, 3, 100, 0), inclui: [] },
25
+ { id: 'studio', nome: 'Primo Studio', preco: 599.9, recursos: R(800, 2160, false, true, true, true, 3, 8, 0, 0), inclui: [] },
26
26
  ];
27
27
 
28
28
  function buscarPlanos(servidor, { timeout = 8000 } = {}) {
@@ -60,7 +60,9 @@ const LINHAS = [
60
60
  ['Projetos do Clube', (r) => (r.clubeCompleto ? 'completos' : 'resumo')],
61
61
  ['Scripts AE modelo', (r) => (r.scriptsAE ? '✓' : '—')],
62
62
  ['Prioridade na cadeia', (r) => (r.prioridade ? '●'.repeat(r.prioridade) : '—')],
63
- ['Subagentes', (r) => (r.subagentes ? String(r.subagentes) : '—')],
63
+ ['Subagentes juntos', (r) => (r.subagentes ? String(r.subagentes) : 'em fila')],
64
+ ['Kits de marca', (r) => (r.kitsMarca == null || r.kitsMarca === 0 ? 'sem teto' : String(r.kitsMarca))],
65
+ ['Edições por vídeo', (r) => (r.versoesEditor == null || r.versoesEditor === 0 ? 'sem teto' : String(r.versoesEditor))],
64
66
  ];
65
67
 
66
68
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "10.14.0",
3
+ "version": "10.14.1",
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": {
@@ -10,7 +10,7 @@
10
10
  "start": "node bin/primocode.js",
11
11
  "postinstall": "node lib/preparar.js || true",
12
12
  "test:server": "cd server && npm test",
13
- "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/mcp-http.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 && node test/effectcraft.test.js && node test/projetos.test.js && node test/maquina.test.js && node test/maquina-som.test.js && node test/maquina-voz.test.js && node test/prova-fresca.test.js && node test/cota-video.test.js && node test/hooks.test.js && node test/modo-plano.test.js && node test/headless.test.js && node test/retomar.test.js && node test/sandbox.test.js && node test/exportar.test.js && node test/editor-video.test.js && node test/marca-kit.test.js && node test/preparar.test.js"
13
+ "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/mcp-http.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 && node test/effectcraft.test.js && node test/projetos.test.js && node test/maquina.test.js && node test/maquina-som.test.js && node test/maquina-voz.test.js && node test/prova-fresca.test.js && node test/cota-video.test.js && node test/hooks.test.js && node test/modo-plano.test.js && node test/headless.test.js && node test/retomar.test.js && node test/sandbox.test.js && node test/exportar.test.js && node test/editor-video.test.js && node test/marca-kit.test.js && node test/preparar.test.js && node test/landing-planos.test.js && node test/limites.test.js"
14
14
  },
15
15
  "engines": {
16
16
  "node": ">=18.17.0"