primocode 8.22.1 → 8.25.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/lib/catalogo.js +2 -0
- package/lib/config.js +53 -2
- package/lib/studio.js +125 -11
- package/lib/tools.js +35 -1
- package/package.json +2 -2
- package/studio/__pycache__/voz.cpython-311.pyc +0 -0
- package/studio/arquivos/corte-estatico-corte-a5bab.json +150 -0
- package/studio/arquivos/corte-founders-ai-teste-do-player-2de1d.json +65 -0
- package/studio/arquivos/corte-legado-teste.json +1 -0
- package/studio/arquivos/corte-legado2.json +1 -0
- package/studio/arquivos/corte-minimo-2a829.json +24 -0
- package/studio/arquivos/corte-ok-00a3b.json +18 -0
- package/studio/arquivos/corte-ok-1270e.json +22 -0
- package/studio/arquivos/corte-sweep-corte-cfb29.json +147 -0
- package/studio/arquivos/corte-visual-completo-78f50.json +35 -0
- package/studio/arquivos/grade-sweep-grade-0c2f1.json +119 -0
- package/studio/arquivos/prisma-estatico-prisma-d8740.json +182 -0
- package/studio/arquivos/prisma-sweep-prisma-4c58c.json +166 -0
- package/studio/arquivos/prisma-y-b0785.json +52 -0
- package/studio/arquivos/prosa-sweep-prosa-f29cf.json +80 -0
- package/studio/arquivos/tela-estatico-tela-c7daf.json +90 -0
- package/studio/arquivos/tela-sweep-tela-0d2bf.json +84 -0
- package/studio/arquivos/traco-estatico-traco-25872.json +113 -0
- package/studio/arquivos/traco-sweep-traco-0bcf4.json +102 -0
- package/studio/ferramentas/corte.md +159 -12
- package/studio/server.py +264 -8
- package/studio/web/analise.html +52 -0
- package/studio/web/js/analise.js +202 -0
- package/studio/web/js/exportar.js +88 -1
- package/studio/web/js/player.js +172 -46
- package/studio/web/js/render.js +99 -1
- package/studio/web/js/video.js +434 -0
- package/studio/web/render.css +27 -0
- package/studio/web/tool.html +1 -0
- package/studio/web/view.html +2 -0
package/lib/catalogo.js
CHANGED
|
@@ -89,6 +89,8 @@ const GRUPOS = [
|
|
|
89
89
|
{ tool: 'studio_listar', escreve: false, faz: 'lista o que já existe no estúdio', args: null },
|
|
90
90
|
{ tool: 'studio_midia', escreve: false, faz: 'manda imagem, música ou vídeo seu para usar nas peças',
|
|
91
91
|
args: 'arquivo (opcional): caminho no projeto — sem ele, lista a galeria. nome (opcional).' },
|
|
92
|
+
{ tool: 'studio_analisar', escreve: false, faz: 'ouve um vídeo já enviado e diz onde estão as pausas, para cortá-las',
|
|
93
|
+
args: 'arquivo: a URL "/midia/..." do vídeo. pausa (opcional): segundos a partir dos quais conta como pausa (padrão 0.6). Devolve "trechos": vire cada um numa cena com {"tipo":"clipe","de":...,"ate":...}.' },
|
|
92
94
|
],
|
|
93
95
|
},
|
|
94
96
|
{
|
package/lib/config.js
CHANGED
|
@@ -135,9 +135,31 @@ function findSimilarProject(projectName, minScore = 0.4) {
|
|
|
135
135
|
return best;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
/* A memória geral como o modelo precisa vê-la: o começo E o fim.
|
|
139
|
+
*
|
|
140
|
+
* Isto lia os PRIMEIROS 2000 caracteres — que são o cabeçalho do template.
|
|
141
|
+
* As anotações vão para o FIM do arquivo, então tudo que o agente aprendia
|
|
142
|
+
* ficava fora do corte e nunca chegava ao modelo. O usuário via o sintoma
|
|
143
|
+
* exato: "nunca lembra do que fez no chat anterior". O arquivo enchia, a
|
|
144
|
+
* poda funcionava, e nada daquilo era lido.
|
|
145
|
+
*
|
|
146
|
+
* Agora vêm as duas pontas: o cabeçalho (o que a PESSOA escreveu sobre si,
|
|
147
|
+
* que é permanente) e as anotações mais recentes (o que o agente aprendeu,
|
|
148
|
+
* que é o que muda). O meio é o que se perde — e é o certo a perder. */
|
|
149
|
+
function readGeneralMemory(limit = 2600) {
|
|
150
|
+
let texto;
|
|
151
|
+
try { texto = fs.readFileSync(GENERAL_MEMORY, 'utf-8'); }
|
|
140
152
|
catch { return ''; }
|
|
153
|
+
if (texto.length <= limit) return texto;
|
|
154
|
+
|
|
155
|
+
const blocos = texto.split(/\n(?=### )/);
|
|
156
|
+
const cabeca = (blocos.shift() || '').slice(0, Math.floor(limit * 0.45));
|
|
157
|
+
let recente = '';
|
|
158
|
+
for (let i = blocos.length - 1; i >= 0; i--) {
|
|
159
|
+
if (cabeca.length + recente.length + blocos[i].length > limit) break;
|
|
160
|
+
recente = '\n' + blocos[i] + recente;
|
|
161
|
+
}
|
|
162
|
+
return cabeca + (recente ? '\n' + recente : '');
|
|
141
163
|
}
|
|
142
164
|
|
|
143
165
|
// Recebem o DIRETÓRIO de trabalho, não o nome do projeto: quando o PrimoCode
|
|
@@ -156,6 +178,34 @@ function appendProjectMemory(dir, entry) {
|
|
|
156
178
|
} catch {}
|
|
157
179
|
}
|
|
158
180
|
|
|
181
|
+
/* O que o usuário JÁ criou no estúdio, para "lembra do vídeo tal" funcionar.
|
|
182
|
+
*
|
|
183
|
+
* A memória geral guarda "pedido → resultado" em texto corrido, e o id da
|
|
184
|
+
* peça se perdia no meio da frase. O dono pediu:
|
|
185
|
+
*
|
|
186
|
+
* "Ter memória entre os chats. Ela lembrar do que fez no chat anterior.
|
|
187
|
+
* Quando eu falar 'lembra do vídeo tal', ela vai lembrar. Na estrutura de
|
|
188
|
+
* arquivos, eu quero que você crie para mim a partir de vídeos, não só de
|
|
189
|
+
* código."
|
|
190
|
+
*
|
|
191
|
+
* Uma linha por peça, com tipo, título e id — que é o que o agente precisa
|
|
192
|
+
* para chamar studio_atualizar ou abrir de novo. Sem link: ele é derivável
|
|
193
|
+
* do id e ocuparia espaço em todo pedido seguinte, para sempre.
|
|
194
|
+
*
|
|
195
|
+
* A entrada é DEDUPLICADA por id: recriar ou atualizar a mesma peça não pode
|
|
196
|
+
* encher a memória de cópias — foi o que aconteceria num vídeo ajustado dez
|
|
197
|
+
* vezes numa tarefa só. */
|
|
198
|
+
function lembrarCriacao({ ferramenta, titulo, id }) {
|
|
199
|
+
if (!id) return;
|
|
200
|
+
const linha = `- criou ${ferramenta || 'peça'} "${String(titulo || 'sem título').slice(0, 70)}" — id \`${id}\``;
|
|
201
|
+
try {
|
|
202
|
+
ensureStructure();
|
|
203
|
+
const texto = fs.existsSync(GENERAL_MEMORY) ? fs.readFileSync(GENERAL_MEMORY, 'utf-8') : '';
|
|
204
|
+
if (texto.includes(`\`${id}\``)) return; // já anotada
|
|
205
|
+
appendGeneralMemory(linha);
|
|
206
|
+
} catch {}
|
|
207
|
+
}
|
|
208
|
+
|
|
159
209
|
// A contraparte de appendProjectMemory, para o que vale em TODOS os projetos
|
|
160
210
|
// (nome do usuário, preferência, decisão de longo prazo). Existia a promessa
|
|
161
211
|
// no prompt — "escreve aqui o que descobrir sobre você" — mas nenhuma função
|
|
@@ -240,4 +290,5 @@ module.exports = {
|
|
|
240
290
|
ensureStructure, slugify, stripAccents,
|
|
241
291
|
createProjectDir, getProjectDir, listProjects, findSimilarProject,
|
|
242
292
|
readGeneralMemory, readProjectMemory, appendProjectMemory, appendGeneralMemory,
|
|
293
|
+
lembrarCriacao,
|
|
243
294
|
};
|
package/lib/studio.js
CHANGED
|
@@ -412,9 +412,32 @@ const TIPOS_MIDIA = {
|
|
|
412
412
|
|
|
413
413
|
const midiaListar = () => comStudio(async () => ({ ok: true, midia: await pedir('GET', '/api/midia') }));
|
|
414
414
|
|
|
415
|
+
/* O caminho como ele chega quando a pessoa ARRASTA o arquivo para o terminal.
|
|
416
|
+
*
|
|
417
|
+
* "Eu quero que eu consiga arrastar os meus vídeos pra dentro do terminal."
|
|
418
|
+
* Arrastar, num terminal, cola o caminho — e cola do jeito do sistema: o
|
|
419
|
+
* macOS escapa espaço com barra invertida, o Windows põe aspas em volta, e
|
|
420
|
+
* alguns colam file:// na frente. `path.resolve` de qualquer um desses dá um
|
|
421
|
+
* caminho que não existe, e a resposta era "arquivo não existe" para um
|
|
422
|
+
* arquivo que está bem ali.
|
|
423
|
+
*/
|
|
424
|
+
function limparCaminho(bruto) {
|
|
425
|
+
let s = String(bruto || '').trim();
|
|
426
|
+
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
427
|
+
s = s.slice(1, -1);
|
|
428
|
+
}
|
|
429
|
+
if (/^file:\/\//i.test(s)) {
|
|
430
|
+
try { s = decodeURIComponent(s.replace(/^file:\/\//i, '')); } catch { /* fica como está */ }
|
|
431
|
+
}
|
|
432
|
+
// Só desescapa o que o shell escapa; um "\" de caminho do Windows fica.
|
|
433
|
+
s = s.replace(/\\([ ()'"&!$`\\])/g, '$1');
|
|
434
|
+
if (s.startsWith('~/') || s === '~') s = path.join(os.homedir(), s.slice(1));
|
|
435
|
+
return s.trim();
|
|
436
|
+
}
|
|
437
|
+
|
|
415
438
|
const midiaEnviar = ({ arquivo, nome }) => comStudio(async () => {
|
|
416
439
|
if (!arquivo) return { ok: false, error: 'Diga o caminho do arquivo (arquivo: "foto.jpg").' };
|
|
417
|
-
const caminho = path.resolve(arquivo);
|
|
440
|
+
const caminho = path.resolve(limparCaminho(arquivo));
|
|
418
441
|
if (!fs.existsSync(caminho)) {
|
|
419
442
|
return {
|
|
420
443
|
ok: false,
|
|
@@ -430,22 +453,113 @@ const midiaEnviar = ({ arquivo, nome }) => comStudio(async () => {
|
|
|
430
453
|
return { ok: false, error: `Formato não aceito: ${ext}`, aceitos: Object.keys(TIPOS_MIDIA).join(' ') };
|
|
431
454
|
}
|
|
432
455
|
const bytes = fs.statSync(caminho).size;
|
|
433
|
-
|
|
456
|
+
const ehVideo = ext === '.mp4' || ext === '.webm' || ext === '.mov';
|
|
457
|
+
const teto = ehVideo ? 2 * 1024 * 1024 * 1024 : 60 * 1024 * 1024;
|
|
458
|
+
if (bytes > teto) {
|
|
459
|
+
return { ok: false, error: `Arquivo tem ${(bytes / 1048576).toFixed(1)} MB; `
|
|
460
|
+
+ `o limite é ${Math.round(teto / 1048576)} MB.` };
|
|
461
|
+
}
|
|
434
462
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
463
|
+
// O corpo vai em pedaços, direto do disco para o socket. Em base64 um
|
|
464
|
+
// vídeo de celular precisaria caber inteiro na memória três vezes — e o
|
|
465
|
+
// Node estourava antes de o estúdio ver um byte.
|
|
466
|
+
const r = await enviarArquivo(caminho, nome || path.basename(caminho));
|
|
467
|
+
const comoUsar = ehVideo
|
|
468
|
+
? `Use "${r.url}" no "src" de um elemento {"tipo":"clipe"}. Para cortar as pausas, `
|
|
469
|
+
+ 'escreva VÁRIOS clipes do mesmo src, cada um com "de" e "ate" em segundos.'
|
|
470
|
+
: `Use "${r.url}" em fundo.imagem, em src de uma imagem, ou em trilha.src.`;
|
|
471
|
+
return { ok: true, ...r, comoUsar };
|
|
472
|
+
});
|
|
440
473
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return
|
|
474
|
+
/* PUT /api/midia/<nome> com o arquivo cru no corpo. */
|
|
475
|
+
function enviarArquivo(caminho, nome) {
|
|
476
|
+
return new Promise((resolve, reject) => {
|
|
477
|
+
const bytes = fs.statSync(caminho).size;
|
|
478
|
+
const req = http.request({
|
|
479
|
+
hostname: '127.0.0.1', port: PORTA, method: 'PUT',
|
|
480
|
+
path: '/api/midia/' + encodeURIComponent(nome),
|
|
481
|
+
// Vídeo grande copiando para o disco leva tempo; meia hora é
|
|
482
|
+
// folga para um arquivo de duas horas, e continua sendo um teto.
|
|
483
|
+
timeout: 30 * 60 * 1000,
|
|
484
|
+
headers: { 'X-Primo-Agente': '1', 'Content-Type': 'application/octet-stream',
|
|
485
|
+
'Content-Length': bytes },
|
|
486
|
+
}, (res) => {
|
|
487
|
+
let texto = '';
|
|
488
|
+
res.setEncoding('utf8');
|
|
489
|
+
res.on('data', (c) => { texto += c; });
|
|
490
|
+
res.on('end', () => {
|
|
491
|
+
let corpo = null;
|
|
492
|
+
try { corpo = JSON.parse(texto); } catch { /* texto cru serve */ }
|
|
493
|
+
if (res.statusCode >= 400) {
|
|
494
|
+
return reject(new Error((corpo && corpo.erro) || texto.slice(0, 300)));
|
|
495
|
+
}
|
|
496
|
+
resolve(corpo || {});
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
req.on('timeout', () => { req.destroy(new Error('o envio demorou demais')); });
|
|
500
|
+
req.on('error', reject);
|
|
501
|
+
const leitura = fs.createReadStream(caminho);
|
|
502
|
+
leitura.on('error', (e) => { req.destroy(e); reject(e); });
|
|
503
|
+
leitura.pipe(req);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* ── Ouvir o vídeo para achar as pausas ──────────────────────────────────
|
|
508
|
+
*
|
|
509
|
+
* "Ele vai cortar as pausas demoradas no vídeo."
|
|
510
|
+
*
|
|
511
|
+
* Cortar pausa exige saber onde a fala para, e nada aqui lê o áudio de um
|
|
512
|
+
* .mp4: o PrimoCode não tem dependência, e o Python da máquina lê WAV e mais
|
|
513
|
+
* nada. ffmpeg resolveria e seria mais uma instalação de 80 MB para quem só
|
|
514
|
+
* quer editar um vídeo.
|
|
515
|
+
*
|
|
516
|
+
* Quem decodifica MP4, MOV e webm já está aberto na mesa. A conta roda numa
|
|
517
|
+
* aba que abre, trabalha e se fecha; aqui a gente espera a resposta.
|
|
518
|
+
*
|
|
519
|
+
* `abrir` é injetado (é a função de abrir navegador do tools.js) para esta
|
|
520
|
+
* camada não passar a conhecer o sistema operacional.
|
|
521
|
+
*/
|
|
522
|
+
const analisarAudio = ({ arquivo, pausa, abrir }) => comStudio(async () => {
|
|
523
|
+
if (!arquivo) return { ok: false, error: 'Diga qual mídia analisar (arquivo: "/midia/x.mp4").' };
|
|
524
|
+
const tarefa = await pedir('POST', '/api/analise', { arquivo, pausa });
|
|
525
|
+
if (abrir) { try { abrir(`${BASE}${tarefa.pagina}`); } catch { /* segue e expira */ } }
|
|
526
|
+
|
|
527
|
+
// Um vídeo de meia hora leva alguns segundos para decodificar; três
|
|
528
|
+
// minutos é folga com sobra, e continua sendo um teto — sem ele um
|
|
529
|
+
// navegador que nunca abriu deixaria a tarefa pendurada para sempre.
|
|
530
|
+
const prazo = Date.now() + 180000;
|
|
531
|
+
while (Date.now() < prazo) {
|
|
532
|
+
await new Promise((ok) => setTimeout(ok, 700));
|
|
533
|
+
const estado = await pedir('GET', `/api/analise/${tarefa.id}`, null, 8000).catch(() => null);
|
|
534
|
+
if (!estado) continue;
|
|
535
|
+
if (estado.estado === 'erro') {
|
|
536
|
+
return { ok: false, error: (estado.resultado && estado.resultado.erro) || 'a análise falhou' };
|
|
537
|
+
}
|
|
538
|
+
if (estado.estado === 'pronto') {
|
|
539
|
+
const r = estado.resultado || {};
|
|
540
|
+
return {
|
|
541
|
+
ok: true,
|
|
542
|
+
duracao: r.duracao,
|
|
543
|
+
cortado: r.cortado,
|
|
544
|
+
pausas: r.pausas,
|
|
545
|
+
trechos: r.trechos,
|
|
546
|
+
comoUsar: `${(r.trechos || []).length} trecho(s) sem pausa. Escreva UMA CENA por trecho, `
|
|
547
|
+
+ `cada uma com {"tipo":"clipe","src":"${arquivo}","de":<início>,"ate":<fim>}. `
|
|
548
|
+
+ 'NÃO escreva "duracao" nessas cenas: a cena já dura o trecho.',
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
ok: false,
|
|
554
|
+
error: 'A aba de análise não respondeu em 3 minutos. Ela precisa do navegador aberto. '
|
|
555
|
+
+ 'Siga sem o corte automático: escreva os trechos que o usuário pedir, ou use o vídeo inteiro '
|
|
556
|
+
+ 'num clipe só (sem "de"/"ate").',
|
|
557
|
+
};
|
|
444
558
|
});
|
|
445
559
|
|
|
446
560
|
module.exports = {
|
|
447
561
|
BASE, PORTA, CASA,
|
|
448
562
|
garantirNoAr, estaNoAr, prepararCasa, pythonCmd,
|
|
449
563
|
manual, ferramentas, spec, listar, ler, criar, atualizar, versoesDe,
|
|
450
|
-
midiaListar, midiaEnviar,
|
|
564
|
+
midiaListar, midiaEnviar, analisarAudio,
|
|
451
565
|
};
|
package/lib/tools.js
CHANGED
|
@@ -172,6 +172,8 @@ async function runShell(command, timeout = 60000, cwd) {
|
|
|
172
172
|
// `https://x/"; touch /tmp/pwned; echo "` injetava comando (achado de segurança).
|
|
173
173
|
// Quando o auto-abrir do Studio abriu pela última vez — o freio das mil abas.
|
|
174
174
|
let ultimaAberturaStudio = 0;
|
|
175
|
+
// Uma rodada de melhoria por tarefa (ver o aviso em studio_criar).
|
|
176
|
+
let melhorouStudio = false;
|
|
175
177
|
|
|
176
178
|
function openInBrowser(target) {
|
|
177
179
|
const p = os.platform();
|
|
@@ -513,6 +515,13 @@ async function despachar(name, args, ctx) {
|
|
|
513
515
|
// saber abrir". E cada "abre pra mim" digitado depois custava
|
|
514
516
|
// uma tarefa inteira de tokens para o modelo redescobrir o
|
|
515
517
|
// link. Abrir é determinístico: não se gasta modelo com isso.
|
|
518
|
+
// Fica anotado na memória geral: é assim que "lembra do
|
|
519
|
+
// vídeo tal" funciona na conversa seguinte. O id é o que
|
|
520
|
+
// permite reabrir ou ajustar sem recriar.
|
|
521
|
+
if (r && r.ok !== false && r.id) {
|
|
522
|
+
cfg.lembrarCriacao({ ferramenta: r.ferramenta || args.ferramenta,
|
|
523
|
+
titulo: r.titulo || args.titulo, id: r.id });
|
|
524
|
+
}
|
|
516
525
|
if (r && r.ok !== false && r.ver) {
|
|
517
526
|
// UMA aba por tarefa. O modelo às vezes cria três versões
|
|
518
527
|
// seguidas, e abrir todas enchia o navegador do usuário de
|
|
@@ -523,7 +532,24 @@ async function despachar(name, args, ctx) {
|
|
|
523
532
|
ultimaAberturaStudio = agora;
|
|
524
533
|
try { openInBrowser(r.ver); } catch {}
|
|
525
534
|
r.abertoNoNavegador = true;
|
|
526
|
-
|
|
535
|
+
// O estúdio revisa a peça e devolve o que ficou
|
|
536
|
+
// monótono (mesmo fundo em tudo, transição repetida,
|
|
537
|
+
// sem trilha). Sem este ramo o aviso mandaria "chame
|
|
538
|
+
// finish, não ajuste" — e a revisão morreria ali, que
|
|
539
|
+
// é justamente o "fica tudo genérico".
|
|
540
|
+
//
|
|
541
|
+
// UMA rodada de conserto, e acabou: mais que isso
|
|
542
|
+
// vira o agente polindo sozinho enquanto o usuário
|
|
543
|
+
// espera.
|
|
544
|
+
if (Array.isArray(r.podeMelhorar) && r.podeMelhorar.length && !melhorouStudio) {
|
|
545
|
+
melhorouStudio = true;
|
|
546
|
+
r.aviso = 'JÁ ABERTO no navegador. Antes de finalizar, faça UMA correção: '
|
|
547
|
+
+ `use studio_atualizar no id "${r.id}" mandando SÓ as chaves que mudam, `
|
|
548
|
+
+ 'para resolver os pontos de "podeMelhorar". Depois chame finish. '
|
|
549
|
+
+ 'Não crie outro arquivo e não repita esta correção.';
|
|
550
|
+
} else {
|
|
551
|
+
r.aviso = 'JÁ ABERTO no navegador do usuário. Chame finish AGORA dizendo o que foi criado — não abra de novo, não leia de volta, não ajuste sem pedido.';
|
|
552
|
+
}
|
|
527
553
|
} else {
|
|
528
554
|
r.aviso = `Você JÁ criou uma versão nesta tarefa, e ela está aberta no navegador. NÃO crie outra: `
|
|
529
555
|
+ `para mudar algo, use studio_atualizar com o id "${r.id}". Chame finish AGORA.`;
|
|
@@ -544,6 +570,14 @@ async function despachar(name, args, ctx) {
|
|
|
544
570
|
return args.arquivo
|
|
545
571
|
? studio.midiaEnviar({ arquivo: resolveInProject(args.arquivo, projectDir), nome: args.nome })
|
|
546
572
|
: studio.midiaListar();
|
|
573
|
+
case 'studio_analisar':
|
|
574
|
+
// Ouvir o vídeo para achar as pausas. A conta acontece no
|
|
575
|
+
// navegador (é quem sabe decodificar MP4 sem instalar nada) —
|
|
576
|
+
// por isso passa a função de abrir, e por isso demora alguns
|
|
577
|
+
// segundos em vez de responder na hora.
|
|
578
|
+
return studio.analisarAudio({
|
|
579
|
+
arquivo: args.arquivo, pausa: args.pausa, abrir: openInBrowser,
|
|
580
|
+
});
|
|
547
581
|
|
|
548
582
|
case 'desktop_double_click':
|
|
549
583
|
return desktop.doubleClick(args.x, args.y);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.25.0",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. 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/modo.test.js && node test/pasta.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/effort.test.js && node test/claude-engine.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/studio-spec.test.js && node test/studio-versoes.test.js && node test/voz.test.js && node test/editor.test.js && node test/estudio-projeto.test.js && node test/vocabulario.test.js"
|
|
11
|
+
"test": "node test/tools.test.js && node test/ui.test.js && node test/modo.test.js && node test/pasta.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/effort.test.js && node test/claude-engine.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/studio-spec.test.js && node test/studio-versoes.test.js && node test/voz.test.js && node test/editor.test.js && node test/estudio-projeto.test.js && node test/vocabulario.test.js && node test/video-export.test.js && node test/monotonia.test.js && node test/clipe.test.js && node test/midia-envio.test.js && node test/pausas.test.js"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=18.17.0"
|
|
Binary file
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "corte-estatico-corte-a5bab",
|
|
3
|
+
"ferramenta": "corte",
|
|
4
|
+
"titulo": "estatico-corte",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "brasa",
|
|
7
|
+
"largura": 1080,
|
|
8
|
+
"altura": 1920,
|
|
9
|
+
"acabamento": "grao",
|
|
10
|
+
"trilha": {
|
|
11
|
+
"tipo": "gerada",
|
|
12
|
+
"estilo": "pulso",
|
|
13
|
+
"volume": 0.7
|
|
14
|
+
},
|
|
15
|
+
"cenas": [
|
|
16
|
+
{
|
|
17
|
+
"layout": "centro",
|
|
18
|
+
"duracao": 3,
|
|
19
|
+
"fundo": "aurora",
|
|
20
|
+
"transicao": "flash",
|
|
21
|
+
"elementos": [
|
|
22
|
+
{
|
|
23
|
+
"tipo": "texto",
|
|
24
|
+
"papel": "titulo",
|
|
25
|
+
"texto": "Sua conta subiu\nde novo.",
|
|
26
|
+
"tamanho": 124,
|
|
27
|
+
"estilo": "gradiente",
|
|
28
|
+
"anim": "nenhuma"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"layout": "centro",
|
|
34
|
+
"duracao": 3,
|
|
35
|
+
"fundo": "vinheta",
|
|
36
|
+
"transicao": "limpar",
|
|
37
|
+
"elementos": [
|
|
38
|
+
{
|
|
39
|
+
"tipo": "texto",
|
|
40
|
+
"papel": "titulo",
|
|
41
|
+
"texto": "E não foi o seu chuveiro.",
|
|
42
|
+
"tamanho": 78,
|
|
43
|
+
"estilo": "marcador",
|
|
44
|
+
"anim": "nenhuma"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"layout": "centro",
|
|
50
|
+
"duracao": 4,
|
|
51
|
+
"transicao": "zoom",
|
|
52
|
+
"acabamento": "vinheta",
|
|
53
|
+
"fundo": {
|
|
54
|
+
"imagem": "/midia/torre.jpg",
|
|
55
|
+
"escurecer": 0.5
|
|
56
|
+
},
|
|
57
|
+
"elementos": [
|
|
58
|
+
{
|
|
59
|
+
"tipo": "numeros",
|
|
60
|
+
"contar": true,
|
|
61
|
+
"anim": "nenhuma",
|
|
62
|
+
"tamanho": 180,
|
|
63
|
+
"itens": [
|
|
64
|
+
"+38% | bandeira tarifária em 12 meses"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"layout": "livre",
|
|
71
|
+
"duracao": 4,
|
|
72
|
+
"transicao": "deslizar",
|
|
73
|
+
"elementos": [
|
|
74
|
+
{
|
|
75
|
+
"tipo": "imagem",
|
|
76
|
+
"src": "/midia/campo.jpg",
|
|
77
|
+
"x": 0,
|
|
78
|
+
"y": 0,
|
|
79
|
+
"l": 1080,
|
|
80
|
+
"a": 1920,
|
|
81
|
+
"raio": 0,
|
|
82
|
+
"movimento": "aproximar",
|
|
83
|
+
"filtro": "contraste",
|
|
84
|
+
"anim": "nenhuma"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"tipo": "forma",
|
|
88
|
+
"forma": "retangulo",
|
|
89
|
+
"x": 0,
|
|
90
|
+
"y": 1180,
|
|
91
|
+
"l": 1080,
|
|
92
|
+
"a": 740,
|
|
93
|
+
"preenchimento": "rgba(10,5,6,.72)",
|
|
94
|
+
"anim": "nenhuma"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"tipo": "texto",
|
|
98
|
+
"x": 90,
|
|
99
|
+
"y": 1320,
|
|
100
|
+
"l": 900,
|
|
101
|
+
"a": 420,
|
|
102
|
+
"texto": "O custo está\nna transmissão",
|
|
103
|
+
"tamanho": 104,
|
|
104
|
+
"peso": 750,
|
|
105
|
+
"estilo": "sombra",
|
|
106
|
+
"anim": "nenhuma"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"layout": "centro",
|
|
112
|
+
"duracao": 5,
|
|
113
|
+
"fundo": "raios",
|
|
114
|
+
"transicao": "subir",
|
|
115
|
+
"elementos": [
|
|
116
|
+
{
|
|
117
|
+
"tipo": "lista",
|
|
118
|
+
"revelar": true,
|
|
119
|
+
"tamanho": 52,
|
|
120
|
+
"intervalo": 0.9,
|
|
121
|
+
"itens": [
|
|
122
|
+
"Linhas saturadas no Nordeste",
|
|
123
|
+
"Usina gerando sem escoar",
|
|
124
|
+
"A conta chega em quem consome"
|
|
125
|
+
],
|
|
126
|
+
"anim": "nenhuma"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"layout": "centro",
|
|
132
|
+
"duracao": 3,
|
|
133
|
+
"fundo": "brilho",
|
|
134
|
+
"transicao": "fade",
|
|
135
|
+
"acabamento": "luz",
|
|
136
|
+
"elementos": [
|
|
137
|
+
{
|
|
138
|
+
"tipo": "texto",
|
|
139
|
+
"papel": "titulo",
|
|
140
|
+
"texto": "Segue para\nentender a próxima.",
|
|
141
|
+
"tamanho": 96,
|
|
142
|
+
"estilo": "contorno-tom",
|
|
143
|
+
"anim": "nenhuma"
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
"atualizado": "2026-08-16T15:08:28+00:00"
|
|
150
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "corte-founders-ai-teste-do-player-2de1d",
|
|
3
|
+
"ferramenta": "corte",
|
|
4
|
+
"titulo": "Founders AI — teste do player",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "brasa",
|
|
7
|
+
"largura": 1080,
|
|
8
|
+
"altura": 1920,
|
|
9
|
+
"acabamento": "grao",
|
|
10
|
+
"trilha": {
|
|
11
|
+
"tipo": "gerada",
|
|
12
|
+
"estilo": "pulso",
|
|
13
|
+
"volume": 0.7
|
|
14
|
+
},
|
|
15
|
+
"cenas": [
|
|
16
|
+
{
|
|
17
|
+
"layout": "centro",
|
|
18
|
+
"duracao": 3,
|
|
19
|
+
"fundo": "aurora",
|
|
20
|
+
"transicao": "flash",
|
|
21
|
+
"elementos": [
|
|
22
|
+
{
|
|
23
|
+
"tipo": "texto",
|
|
24
|
+
"papel": "titulo",
|
|
25
|
+
"texto": "Imersão\nFounders AI",
|
|
26
|
+
"tamanho": 124,
|
|
27
|
+
"estilo": "gradiente",
|
|
28
|
+
"anim": "desfoque"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"layout": "centro",
|
|
34
|
+
"duracao": 3,
|
|
35
|
+
"fundo": "vinheta",
|
|
36
|
+
"elementos": [
|
|
37
|
+
{
|
|
38
|
+
"tipo": "texto",
|
|
39
|
+
"papel": "titulo",
|
|
40
|
+
"texto": "Paris Group · Chapecó",
|
|
41
|
+
"tamanho": 78,
|
|
42
|
+
"estilo": "marcador",
|
|
43
|
+
"anim": "palavra"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"layout": "centro",
|
|
49
|
+
"duracao": 4,
|
|
50
|
+
"fundo": "raios",
|
|
51
|
+
"elementos": [
|
|
52
|
+
{
|
|
53
|
+
"tipo": "numeros",
|
|
54
|
+
"contar": true,
|
|
55
|
+
"tamanho": 180,
|
|
56
|
+
"itens": [
|
|
57
|
+
"20 | empresários por turma"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"atualizado": "2026-08-16T14:55:24+00:00"
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id": "corte-legado-teste", "ferramenta": "corte", "titulo": "legado", "doc": {"tema": "meia-noite", "largura": 1080, "altura": 1920, "cenas": [{"duracao": "4s", "fundo": "aurora", "elementos": [{"tipo": "titulo", "texto": "Imersão Founders AI"}, {"tipo": "subtitulo", "conteudo": "Paris Group · Chapecó"}]}]}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id": "corte-legado2", "ferramenta": "corte", "titulo": "legado2", "doc": {"tema": "meia-noite", "largura": 1080, "altura": 1920, "cenas": [{"duracao": "4s", "fundo": "aurora", "elementos": [{"tipo": "titulo", "texto": "Imersão Founders AI", "anim": "nenhuma"}, {"tipo": "subtitulo", "conteudo": "Paris Group · Chapecó", "anim": "nenhuma"}]}]}}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "corte-minimo-2a829",
|
|
3
|
+
"ferramenta": "corte",
|
|
4
|
+
"titulo": "minimo",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "claro",
|
|
7
|
+
"largura": 1080,
|
|
8
|
+
"altura": 1920,
|
|
9
|
+
"cenas": [
|
|
10
|
+
{
|
|
11
|
+
"layout": "centro",
|
|
12
|
+
"duracao": 5,
|
|
13
|
+
"elementos": [
|
|
14
|
+
{
|
|
15
|
+
"tipo": "texto",
|
|
16
|
+
"texto": "TESTE VISIVEL",
|
|
17
|
+
"tamanho": 120
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"atualizado": "2026-08-16T14:57:24+00:00"
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "corte-ok-1270e",
|
|
3
|
+
"ferramenta": "corte",
|
|
4
|
+
"titulo": "ok",
|
|
5
|
+
"doc": {
|
|
6
|
+
"tema": "brasa",
|
|
7
|
+
"cenas": [
|
|
8
|
+
{
|
|
9
|
+
"duracao": 3,
|
|
10
|
+
"elementos": [
|
|
11
|
+
{
|
|
12
|
+
"tipo": "texto",
|
|
13
|
+
"papel": "titulo",
|
|
14
|
+
"texto": "Founders AI",
|
|
15
|
+
"anim": "nenhuma"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"atualizado": "2026-08-16T17:50:21+00:00"
|
|
22
|
+
}
|