primocode 8.42.1 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -37
- package/bin/primocode.js +599 -327
- package/lib/acervo.js +8 -1
- package/lib/act.js +205 -114
- package/lib/api.js +35 -4
- package/lib/boas-vindas.js +31 -192
- package/lib/catalogo.js +60 -60
- package/lib/config.js +1 -1
- package/lib/conta.js +3 -24
- package/lib/diff.js +161 -0
- package/lib/diretor.js +37 -47
- package/lib/entrada.js +545 -0
- package/lib/especialistas.js +2 -2
- package/lib/estudio/assets.js +343 -0
- package/lib/estudio/identidade.js +560 -0
- package/lib/estudio/index.js +565 -0
- package/lib/estudio/plano.js +327 -0
- package/lib/estudio/projeto.js +246 -0
- package/lib/estudio/remotion.js +606 -0
- package/lib/estudio/slides.js +190 -0
- package/lib/estudio/video.js +296 -0
- package/lib/fala.js +13 -1
- package/lib/marca.js +4 -1
- package/lib/marketplace.js +189 -0
- package/lib/permissao.js +137 -0
- package/lib/registro.js +229 -0
- package/lib/skills.js +329 -0
- package/lib/tools.js +51 -181
- package/lib/ui.js +7 -3
- package/lib/voz.js +239 -0
- package/package.json +12 -3
- package/skills/acessibilidade/SKILL.md +28 -0
- package/skills/api-que-nao-quebra/SKILL.md +27 -0
- package/skills/depurar-de-verdade/SKILL.md +29 -0
- package/skills/editar-meu-video/SKILL.md +59 -0
- package/skills/escrever-em-portugues/SKILL.md +28 -0
- package/skills/git-limpo/SKILL.md +28 -0
- package/skills/motion-de-primeira/SKILL.md +62 -0
- package/skills/pagina-que-converte/SKILL.md +28 -0
- package/skills/performance-web/SKILL.md +28 -0
- package/skills/revisao-antes-de-entregar/SKILL.md +27 -0
- package/skills/slides-que-prendem/SKILL.md +40 -0
- package/studio/edge.py +16 -66
- package/studio/narrar.py +274 -0
- package/studio/pessoa.py +167 -0
- package/voz/primo_voz/__init__.py +0 -0
- package/voz/primo_voz/__main__.py +325 -0
- package/voz/primo_voz/escuta.py +235 -0
- package/voz/primo_voz/orbe.py +216 -0
- package/lib/audio-livre.js +0 -177
- package/lib/chaves.js +0 -149
- package/lib/claude-engine.js +0 -631
- package/lib/codex-engine.js +0 -208
- package/lib/remotion.js +0 -663
- package/lib/studio.js +0 -960
- package/studio/PRIMOCODE.md +0 -58
- package/studio/README.md +0 -154
- package/studio/__pycache__/edge.cpython-311.pyc +0 -0
- package/studio/__pycache__/elevenlabs.cpython-311.pyc +0 -0
- package/studio/__pycache__/voz.cpython-311.pyc +0 -0
- package/studio/arquivos/corte-teste-de-camera-fb823.json +0 -57
- package/studio/ferramentas/00-protocolo.md +0 -125
- package/studio/ferramentas/01-modelos.md +0 -270
- package/studio/ferramentas/README.md +0 -32
- package/studio/ferramentas/corte.md +0 -919
- package/studio/ferramentas/grade.md +0 -92
- package/studio/ferramentas/prisma.md +0 -107
- package/studio/ferramentas/prosa.md +0 -86
- package/studio/ferramentas/tela.md +0 -93
- package/studio/ferramentas/traco.md +0 -79
- package/studio/server.py +0 -2254
- package/studio/studio.log +0 -57
- package/studio/voz.py +0 -388
- package/studio/web/analise.html +0 -52
- package/studio/web/core.css +0 -1167
- package/studio/web/fontes/bebas.woff2 +0 -0
- package/studio/web/fontes/grotesk-700.woff2 +0 -0
- package/studio/web/fontes/inter-400.woff2 +0 -0
- package/studio/web/fontes/inter-700.woff2 +0 -0
- package/studio/web/fontes/jetbrains-400.woff2 +0 -0
- package/studio/web/fontes/playfair-700.woff2 +0 -0
- package/studio/web/fontes.css +0 -23
- package/studio/web/home.html +0 -217
- package/studio/web/js/analise.js +0 -510
- package/studio/web/js/audio.js +0 -607
- package/studio/web/js/editor.js +0 -1802
- package/studio/web/js/exportar.js +0 -222
- package/studio/web/js/ferramentas.js +0 -108
- package/studio/web/js/icones.js +0 -120
- package/studio/web/js/midia.js +0 -157
- package/studio/web/js/narracao.js +0 -325
- package/studio/web/js/player.js +0 -903
- package/studio/web/js/render-grade.js +0 -243
- package/studio/web/js/render-prosa.js +0 -104
- package/studio/web/js/render.js +0 -1852
- package/studio/web/js/sfx.js +0 -233
- package/studio/web/js/video.js +0 -464
- package/studio/web/js/voz-neural.js +0 -152
- package/studio/web/render.css +0 -1352
- package/studio/web/tf.html +0 -15
- package/studio/web/tool.html +0 -78
- package/studio/web/view.html +0 -27
package/lib/boas-vindas.js
CHANGED
|
@@ -1,48 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* boas-vindas.js — a abertura do PrimoCode.
|
|
2
|
+
* boas-vindas.js — a abertura do PrimoCode, na primeira vez e em toda vez.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* login. Ele vai abrir automaticamente a janela lá no Conecta Primo AI com
|
|
7
|
-
* o token, tudo pra fazer login na conta. Aí fez o login, o terminal checa
|
|
8
|
-
* se sua conta é Premium ou Super. Se for, ele libera para a próxima etapa.
|
|
9
|
-
* Se não, ele fala que você não tem acesso ao PrimoCode, porque tem que ter
|
|
10
|
-
* o plano Premium ou Super.
|
|
4
|
+
* Duas coisas acontecem aqui, nesta ordem: a pessoa entra na conta do Conecta
|
|
5
|
+
* Primo AI, e o plano dela é conferido. Acabou. Não há terceira pergunta.
|
|
11
6
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* pra próxima tela, e aí você seleciona Claude ou GPT — e quando seleciona,
|
|
17
|
-
* ele dá aquele /gpt ou /claude, e vai funcionar como já funciona hoje."
|
|
7
|
+
* ── POR QUE NÃO SE ESCOLHE MAIS A IA ─────────────────────────────────────
|
|
8
|
+
* Existiam duas telas depois do login: "Com qual IA você quer trabalhar?" e
|
|
9
|
+
* "Qual conta?" (Claude ou GPT), que ligavam o `/claude` ou o `/gpt` e faziam
|
|
10
|
+
* o PrimoCode rodar em cima da assinatura da própria pessoa.
|
|
18
11
|
*
|
|
19
|
-
*
|
|
12
|
+
* Isso pedia que alguém que acabou de instalar um produto pago já tivesse
|
|
13
|
+
* OUTRO produto pago instalado e logado — e a resposta certa para quase todo
|
|
14
|
+
* mundo era a primeira opção. Uma pergunta cuja resposta certa é sempre a
|
|
15
|
+
* mesma não é escolha, é obstáculo.
|
|
20
16
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* sozinho) → "✅ Autenticado, redirecionando…" → "Como quer prosseguir?",
|
|
24
|
-
* com as SETINHAS: API padrão, API própria de outra IA, ou uma IA já
|
|
25
|
-
* configurada. Escolheu, está pronto para usar.
|
|
17
|
+
* A API vem configurada no produto. Entrou na conta, pode pedir a primeira
|
|
18
|
+
* coisa.
|
|
26
19
|
*
|
|
27
|
-
* ──
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* ── O BLOQUEIO É DELIBERADO ──────────────────────────────────────────────
|
|
34
|
-
* Sem Premium ou Super, o PrimoCode não trabalha. Isso é uma decisão de
|
|
35
|
-
* produto do dono, tomada com o custo na mesa: quem já instalou no gratuito
|
|
36
|
-
* perde o acesso na atualização. Está escrito aqui para ninguém "consertar"
|
|
37
|
-
* isso mais adiante achando que é bug.
|
|
38
|
-
*
|
|
39
|
-
* O bloqueio é de CORTESIA, não de segurança: este arquivo vai publicado no
|
|
40
|
-
* npm com o código à vista e qualquer um apaga a checagem. Quem segura de
|
|
41
|
-
* verdade é o primocode-server, que é quem tem a chave do provedor.
|
|
42
|
-
*
|
|
43
|
-
* ── POR QUE ISTO É UM MÓDULO ─────────────────────────────────────────────
|
|
44
|
-
* Porque tem decisão dentro, e decisão precisa de teste — e teste não digita
|
|
45
|
-
* em terminal. Aqui a entrada e a saída são funções que quem chama fornece.
|
|
20
|
+
* ── O BLOQUEIO É DE CORTESIA ─────────────────────────────────────────────
|
|
21
|
+
* Este arquivo vai publicado no npm com o código à vista: um `if (pro)` aqui
|
|
22
|
+
* é uma linha que qualquer um apaga. Quem segura de verdade é o
|
|
23
|
+
* primocode-server, que é quem tem a chave do provedor. O que se faz aqui é
|
|
24
|
+
* dizer o que houve e como resolver.
|
|
46
25
|
*/
|
|
47
26
|
|
|
48
27
|
'use strict';
|
|
@@ -51,11 +30,9 @@ const fs = require('fs');
|
|
|
51
30
|
const os = require('os');
|
|
52
31
|
const path = require('path');
|
|
53
32
|
|
|
54
|
-
const chavesModulo = require('./chaves.js');
|
|
55
|
-
|
|
56
33
|
const MARCA = () => path.join(os.homedir(), '.primocode', 'ja-abriu.json');
|
|
57
34
|
|
|
58
|
-
/** Já
|
|
35
|
+
/** Já passou pela abertura alguma vez? A marca não guarda login nenhum. */
|
|
59
36
|
function jaEscolheu() {
|
|
60
37
|
try { return fs.existsSync(MARCA()); } catch { return false; }
|
|
61
38
|
}
|
|
@@ -73,86 +50,6 @@ function esquecer() {
|
|
|
73
50
|
try { fs.rmSync(MARCA(), { force: true }); return true; } catch { return false; }
|
|
74
51
|
}
|
|
75
52
|
|
|
76
|
-
// ── as duas telas de escolha ─────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
const MOTORES = [
|
|
79
|
-
{ id: 'padrao', titulo: 'Continuar com a API padrão',
|
|
80
|
-
detalhe: 'A que já vem pronta, incluída no seu plano. É só começar.' },
|
|
81
|
-
{ id: 'propria', titulo: 'Usar a API própria de outra IA',
|
|
82
|
-
detalhe: 'Você cola a sua chave do Claude ou do GPT, e ela é usada nas chamadas.' },
|
|
83
|
-
{ id: 'configurada', titulo: 'Usar uma IA já configurada',
|
|
84
|
-
detalhe: 'O Claude Code ou o Codex que já está instalado e logado nesta máquina.' },
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
const PROVEDORES = [
|
|
88
|
-
{ id: 'claude', titulo: 'Claude', comando: '/claude', chave: 'claude',
|
|
89
|
-
detalhe: 'Usa o Claude Code da sua conta, no Opus.' },
|
|
90
|
-
{ id: 'gpt', titulo: 'GPT', comando: '/gpt', chave: 'codex',
|
|
91
|
-
detalhe: 'Usa o Codex da sua conta, num modelo barato.' },
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* O que a pessoa digitou → qual item da lista.
|
|
96
|
-
*
|
|
97
|
-
* Aceita o número, o id, o começo do nome, e Enter (que é sempre o primeiro:
|
|
98
|
-
* quem aperta Enter sem ler está dizendo "faz o que for melhor").
|
|
99
|
-
*/
|
|
100
|
-
function escolher(texto, lista) {
|
|
101
|
-
const t = String(texto || '').trim().toLowerCase();
|
|
102
|
-
if (!t) return lista[0].id;
|
|
103
|
-
const n = Number(t);
|
|
104
|
-
if (Number.isInteger(n) && n >= 1 && n <= lista.length) return lista[n - 1].id;
|
|
105
|
-
// Primeiro o que bate INTEIRO. O prefixo vem depois, senão "continuar com
|
|
106
|
-
// a API padrão" cairia em "configurada" só por começar com "con".
|
|
107
|
-
for (const e of lista) if (t === e.id || t === e.titulo.toLowerCase()) return e.id;
|
|
108
|
-
for (const e of lista) if (e.id.length >= 3 && t.startsWith(e.id.slice(0, 3))) return e.id;
|
|
109
|
-
// O jeito como as pessoas falam, e não como a lista se chama.
|
|
110
|
-
if (lista === MOTORES) {
|
|
111
|
-
if (/j[aá] config|j[aá] instal|j[aá] logad|configurad|instalad|logad/.test(t)) return 'configurada';
|
|
112
|
-
if (/padr|inclu|mesmo|pronta|voc[eê]s|de voc/.test(t)) return 'padrao';
|
|
113
|
-
if (/minha|meu|pr[oó]pri|conta|chave|assinatura|api de outra/.test(t)) return 'propria';
|
|
114
|
-
} else {
|
|
115
|
-
if (/claude|anthropic|opus|sonnet/.test(t)) return 'claude';
|
|
116
|
-
if (/gpt|openai|codex|chatgpt/.test(t)) return 'gpt';
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* A escolha na tela. Com terminal de verdade é a LISTA COM AS SETINHAS — ↑↓
|
|
123
|
-
* anda, Enter confirma, como no Claude Code: ninguém precisa saber que o item
|
|
124
|
-
* tem número. `selecionar` vem de quem chamou (o bin, que é dono do teclado);
|
|
125
|
-
* onde ele não existe — teste, pipe, terminal sem raw mode — cai na lista
|
|
126
|
-
* numerada de sempre, que responde ao número, ao nome e ao Enter.
|
|
127
|
-
*/
|
|
128
|
-
async function perguntarDaLista({ perguntar, mostrar, cores: c, titulo, ajuda, lista, selecionar }) {
|
|
129
|
-
if (selecionar) {
|
|
130
|
-
const escolhido = await selecionar({ titulo, ajuda, lista });
|
|
131
|
-
if (escolhido && lista.some((e) => e.id === escolhido)) return escolhido;
|
|
132
|
-
if (escolhido === null) return lista[0].id; // Esc: fica com a primeira
|
|
133
|
-
}
|
|
134
|
-
mostrar('');
|
|
135
|
-
mostrar(c.bold(c.brand(' ' + titulo)));
|
|
136
|
-
if (ajuda) mostrar(c.muted(' ' + ajuda));
|
|
137
|
-
mostrar('');
|
|
138
|
-
lista.forEach((e, i) => {
|
|
139
|
-
mostrar(' ' + c.brand(String(i + 1)) + c.muted('. ') + c.white(e.titulo));
|
|
140
|
-
mostrar(' ' + c.dim(e.detalhe));
|
|
141
|
-
});
|
|
142
|
-
mostrar('');
|
|
143
|
-
|
|
144
|
-
for (let tentativa = 0; tentativa < 3; tentativa++) {
|
|
145
|
-
const dito = await perguntar(c.brand(' ❯ ')
|
|
146
|
-
+ c.muted(`1 a ${lista.length} (Enter = 1) `));
|
|
147
|
-
const qual = escolher(dito, lista);
|
|
148
|
-
if (qual) return qual;
|
|
149
|
-
mostrar(c.muted(` Não entendi. Digite 1 a ${lista.length}.`));
|
|
150
|
-
}
|
|
151
|
-
// Três tentativas sem entender: fica com a primeira em vez de prender
|
|
152
|
-
// alguém numa pergunta para usar o programa.
|
|
153
|
-
return lista[0].id;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
53
|
// ── a abertura ───────────────────────────────────────────────────────────
|
|
157
54
|
|
|
158
55
|
/**
|
|
@@ -164,10 +61,9 @@ async function perguntarDaLista({ perguntar, mostrar, cores: c, titulo, ajuda, l
|
|
|
164
61
|
* @param {()=>any} op.conectado a credencial local, ou null
|
|
165
62
|
* @param {()=>Promise<any>} op.conectar roda o login (abre o navegador)
|
|
166
63
|
* @param {()=>Promise<any>} op.sessao confirma o plano NO SERVIDOR
|
|
167
|
-
* @param {(cmd:string)=>Promise<any>} op.aplicar executa /claude ou /gpt
|
|
168
64
|
*/
|
|
169
65
|
async function rodar({ perguntar, mostrar, cores: c, interativo = true,
|
|
170
|
-
conectado, conectar, sessao
|
|
66
|
+
conectado, conectar, sessao } = {}) {
|
|
171
67
|
|
|
172
68
|
/* Sem terminal de verdade (pipe, script, CI) não se pergunta nada — uma
|
|
173
69
|
pergunta onde ninguém pode responder trava o processo para sempre, e
|
|
@@ -191,12 +87,6 @@ async function rodar({ perguntar, mostrar, cores: c, interativo = true,
|
|
|
191
87
|
mostrar(c.muted(' Tente de novo com ') + c.brand('/connect') + c.muted('.'));
|
|
192
88
|
return { liberado: false, motivo: 'sem-login' };
|
|
193
89
|
}
|
|
194
|
-
/* A MESMA FRASE DOS DOIS LADOS. A página mostra "✅ Autenticado,
|
|
195
|
-
redirecionando…" e some; quem estava olhando o navegador volta ao
|
|
196
|
-
terminal e precisa ver que o terminal soube — senão parece que a
|
|
197
|
-
aprovação ficou no site. */
|
|
198
|
-
mostrar('');
|
|
199
|
-
mostrar(c.ok(' ✅ Autenticado, redirecionando…'));
|
|
200
90
|
credencial = conectado ? conectado() : { plano: r.plano };
|
|
201
91
|
}
|
|
202
92
|
|
|
@@ -235,70 +125,19 @@ async function rodar({ perguntar, mostrar, cores: c, interativo = true,
|
|
|
235
125
|
mostrar('');
|
|
236
126
|
mostrar(c.ok(' Conta liberada.') + c.muted(` Plano ${plano}.`));
|
|
237
127
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
ajuda: 'Use as setinhas ↑↓ e Enter para confirmar — ou digite o número.',
|
|
246
|
-
lista: MOTORES,
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
if (motor === 'padrao') {
|
|
250
|
-
marcar({ motor: 'padrao', plano });
|
|
251
|
-
mostrar('');
|
|
252
|
-
mostrar(c.muted(' Pronto. Pode pedir a primeira coisa.'));
|
|
253
|
-
mostrar('');
|
|
254
|
-
return { liberado: true, motor: 'padrao', plano, perguntou: true };
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ── 4. qual IA ───────────────────────────────────────────────────────
|
|
258
|
-
const propria = motor === 'propria';
|
|
259
|
-
const qual = await perguntarDaLista({
|
|
260
|
-
perguntar, mostrar, cores: c, selecionar,
|
|
261
|
-
titulo: propria ? 'De qual IA é a sua chave?' : 'Qual IA já configurada?',
|
|
262
|
-
ajuda: propria
|
|
263
|
-
? 'A chave fica só nesta máquina, em ~/.primocode/chaves.json.'
|
|
264
|
-
: 'Ele usa o programa que você já tem instalado e logado.',
|
|
265
|
-
lista: PROVEDORES,
|
|
266
|
-
});
|
|
267
|
-
const escolhido = PROVEDORES.find((p) => p.id === qual) || PROVEDORES[0];
|
|
268
|
-
|
|
269
|
-
/* ── A CHAVE PRÓPRIA ─────────────────────────────────────────────────
|
|
270
|
-
* Só neste caminho se pede chave. Quem escolheu "já configurada" não tem
|
|
271
|
-
* chave para colar — a dele está no programa em que já entrou, e pedir
|
|
272
|
-
* uma ali seria inventar um passo que não existe.
|
|
273
|
-
*
|
|
274
|
-
* Chave recusada (colou a do outro provedor, veio truncada) NÃO trava a
|
|
275
|
-
* abertura: diz o que houve, guarda a escolha do motor e segue. O motor
|
|
276
|
-
* roda com o login que a pessoa já tem, e `/chave` conserta depois. */
|
|
277
|
-
if (propria && perguntar) {
|
|
278
|
-
const p = chaves.PROVEDORES[escolhido.chave] || {};
|
|
279
|
-
mostrar('');
|
|
280
|
-
mostrar(c.muted(` Cole a chave do ${escolhido.titulo}`)
|
|
281
|
-
+ c.dim(p.onde ? ` (pegue em ${p.onde})` : '') + c.muted('.'));
|
|
282
|
-
mostrar(c.dim(' Enter em branco pula: dá para configurar depois com ')
|
|
283
|
-
+ c.brand('/chave ' + escolhido.chave) + c.dim('.'));
|
|
284
|
-
const colada = String(await perguntar(c.brand(' ❯ ')) || '').trim();
|
|
285
|
-
if (colada) {
|
|
286
|
-
const g = chaves.guardar(escolhido.chave, colada);
|
|
287
|
-
mostrar(g.ok
|
|
288
|
-
? c.ok(` Chave do ${g.nome} guardada.`) + c.muted(` ${g.disfarce}`)
|
|
289
|
-
: c.err(` ${g.error}`));
|
|
290
|
-
}
|
|
291
|
-
}
|
|
128
|
+
/* Não há etapa 3. O que vinha aqui era a escolha do provedor de IA, e
|
|
129
|
+
ela morreu junto com o `/claude` e o `/gpt`: a API já vem com o
|
|
130
|
+
produto. A marca continua sendo gravada porque é ela que distingue a
|
|
131
|
+
primeira abertura das outras — quem já entrou não precisa ler o texto
|
|
132
|
+
de boas-vindas de novo. */
|
|
133
|
+
const primeira = !jaEscolheu();
|
|
134
|
+
if (primeira) marcar({ plano });
|
|
292
135
|
|
|
293
|
-
marcar({ motor: escolhido.id, origem: motor, plano });
|
|
294
136
|
mostrar('');
|
|
295
|
-
|
|
137
|
+
mostrar(c.muted(primeira ? ' Tudo pronto. Pode pedir a primeira coisa.'
|
|
138
|
+
: ' Pode pedir.'));
|
|
296
139
|
mostrar('');
|
|
297
|
-
return { liberado: true,
|
|
298
|
-
comando: escolhido.comando, plano, perguntou: true };
|
|
140
|
+
return { liberado: true, plano, primeira, perguntou: false };
|
|
299
141
|
}
|
|
300
142
|
|
|
301
|
-
module.exports = {
|
|
302
|
-
rodar, jaEscolheu, marcar, esquecer, escolher,
|
|
303
|
-
MOTORES, PROVEDORES, MARCA,
|
|
304
|
-
};
|
|
143
|
+
module.exports = { rodar, jaEscolheu, marcar, esquecer, MARCA };
|
package/lib/catalogo.js
CHANGED
|
@@ -74,52 +74,67 @@ const GRUPOS = [
|
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
nome: 'Estúdio',
|
|
77
|
-
resumo: '
|
|
77
|
+
resumo: 'vídeo, motion, slides e arte — a IA faz, e no fim abre o resultado pronto',
|
|
78
78
|
itens: [
|
|
79
|
-
{ tool: '
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
+ '
|
|
98
|
-
+ '
|
|
99
|
-
+ '
|
|
100
|
-
|
|
101
|
-
{ tool: '
|
|
102
|
-
args: '
|
|
103
|
-
+ '
|
|
104
|
-
+ '
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
+ '
|
|
111
|
-
+ '
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
+ '
|
|
116
|
-
+ '
|
|
117
|
-
+ '
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{ tool: '
|
|
121
|
-
args: '
|
|
122
|
-
+ '
|
|
79
|
+
{ tool: 'estudio_projeto', escreve: true, faz: 'abre, acha e lista os projetos do estúdio — cada peça é uma pasta que não some',
|
|
80
|
+
args: 'acao: criar | abrir | procurar | listar | anotar. '
|
|
81
|
+
+ 'criar: titulo, pedido, formato (video|slides|arte). '
|
|
82
|
+
+ 'abrir: id — devolve o plano, o CADERNO (o que foi decidido e por quê) e os arquivos. '
|
|
83
|
+
+ 'procurar: texto — acha pelo que o usuário lembra ("aquele do PicPay"), inclusive por detalhe do meio do processo. '
|
|
84
|
+
+ 'SEMPRE procure antes de criar quando o pedido falar de algo que já existe.' },
|
|
85
|
+
|
|
86
|
+
{ tool: 'estudio_plano', escreve: true, faz: 'escreve ou ajusta o que o vídeo É — cenas, elementos, animação, câmera, áudio',
|
|
87
|
+
args: 'id. plano: {formato:{largura,altura,fps}, cenas:[…], audio:{…}} — ou cenas direto. '
|
|
88
|
+
+ 'cena (opcional): o número de UMA cena para trocar só ela, sem reescrever o resto. '
|
|
89
|
+
+ 'motivo (opcional): fica no caderno. voltarPara (opcional): nome de uma versão. '
|
|
90
|
+
+ 'Sem plano nem cenas, LÊ o plano atual. '
|
|
91
|
+
+ 'Cada elemento é {tipo: texto|forma|imagem|video|icone|legenda|componente} com x, y, cor, tamanho e '
|
|
92
|
+
+ '"anima": {propriedade: [{t,v,e}]} — quadros-chave em segundos. A cena tem "camera": {zoom,x,y,rotacao} e '
|
|
93
|
+
+ '"perspectiva" para 3D. NÃO existe layout pronto nem tema — cada propriedade do vídeo é escolha sua, a cada vídeo.' },
|
|
94
|
+
|
|
95
|
+
{ tool: 'estudio_midia', escreve: true, faz: 'procura e baixa ícone, imagem, música e efeito sonoro da internet, e lista as fontes',
|
|
96
|
+
args: 'acao: imagem | icones | som | trilha | efeito | fontes. termo. id (para baixar). '
|
|
97
|
+
+ 'baixar: o número do item da busca (1 é o primeiro). transparente: true para PNG recortado. '
|
|
98
|
+
+ 'icones: sem "nome", procura; com "nome" ("lucide:zap"), baixa. '
|
|
99
|
+
+ 'Tudo é BAIXADO para a pasta do projeto — o plano nunca aponta para a internet.' },
|
|
100
|
+
|
|
101
|
+
{ tool: 'estudio_narrar', escreve: true, faz: 'gera a narração em voz neural e devolve o tempo MEDIDO de cada fala',
|
|
102
|
+
args: 'id. texto. voz (antonio|francisca|thalita). estilo (normal|anuncio|calmo|dramatico). '
|
|
103
|
+
+ 'vozes: true lista as vozes. '
|
|
104
|
+
+ 'Use os tempos que voltam para a duração das cenas — estimar por número de palavras erra em segundos.' },
|
|
105
|
+
|
|
106
|
+
{ tool: 'estudio_video', escreve: true, faz: 'o que se faz com a gravação do usuário: importar, medir, cortar silêncio, transcrever, recortar a pessoa',
|
|
107
|
+
args: 'id. acao: importar | medir | silencio | cortar-silencio | quadro | legendas | recortar-pessoa. '
|
|
108
|
+
+ 'arquivo: caminho (em "importar", de fora; nas outras, o relativo dentro do projeto). '
|
|
109
|
+
+ 'cortar-silencio: minimoParaCortar (padrão 0.6s), respiro (padrão 0.18s) — os dois existem para o corte não soar robótico. '
|
|
110
|
+
+ 'legendas: devolve os trechos com tempo, para virarem elemento "legenda". '
|
|
111
|
+
+ 'recortar-pessoa: devolve um vídeo com fundo TRANSPARENTE, para pôr sobre qualquer fundo.' },
|
|
112
|
+
|
|
113
|
+
{ tool: 'estudio_render', escreve: true, faz: 'vira arquivo pelo Remotion e abre para o usuário: .mp4 no vídeo, .pdf em slides e arte',
|
|
114
|
+
args: 'id. paginas: true força páginas paradas em vez de vídeo (projeto de formato "slides" ou "arte" já faz isso sozinho). '
|
|
115
|
+
+ 'somenteProjeto: true escreve o projeto Remotion sem renderizar. minutos (padrão 30). '
|
|
116
|
+
+ 'A primeira render de um projeto instala o Remotion na pasta dele (alguns minutos, uma vez). '
|
|
117
|
+
+ 'Slide é uma CENA PARADA: o mesmo plano, as mesmas primitivas, e a página sai do quadro do MEIO da cena — '
|
|
118
|
+
+ 'do primeiro sairia em branco, com a animação de entrada ainda fora do quadro.' },
|
|
119
|
+
|
|
120
|
+
{ tool: 'estudio_site', escreve: false, faz: 'lê a identidade visual de uma marca: cores, fontes, logo e o que ela diz de si',
|
|
121
|
+
args: 'url — OU marca: o nome da empresa, quando não há link ("faça um vídeo para a Paris Group"). '
|
|
122
|
+
+ 'id (opcional): anota no caderno do projeto. '
|
|
123
|
+
+ 'Devolve FATOS medidos, não um roteiro — o roteiro é seu. '
|
|
124
|
+
+ 'Use SEMPRE que o pedido citar uma empresa de verdade: inventar a cor de uma marca que existe '
|
|
125
|
+
+ 'é erro que sai pronto no vídeo entregue. '
|
|
126
|
+
+ 'Com id, BAIXA a logo para midia/ e devolve o src pronto — leia o "aviso" dela antes de usar: '
|
|
127
|
+
+ 'a logo pode ser o símbolo monocromático, ou uma imagem que não é logo nenhuma.' },
|
|
128
|
+
|
|
129
|
+
{ tool: 'planejar_producao', escreve: false, noLoop: true,
|
|
130
|
+
faz: 'ativa o DIRETOR: arco, narração cena a cena, voz, direção de arte, mídia a buscar e trilha — a estrutura ANTES de produzir',
|
|
131
|
+
args: 'pedido: o que produzir, como o usuário falou. segundos (opcional, padrão 30). '
|
|
132
|
+
+ 'formato (opcional): video | apresentacao. contexto (opcional): o que você já apurou (identidade da marca, o que o site diz). '
|
|
133
|
+
+ 'Devolve a PAUTA em prosa, mais voz, mídia a buscar e trilha. '
|
|
134
|
+
+ 'Chame ANTES de estudio_plano em qualquer peça que tenha narração ou mais de três cenas: '
|
|
135
|
+
+ 'quem monta o JSON das cenas gasta a atenção no JSON, e é ali que o roteiro vira legenda '
|
|
136
|
+
+ '("Vantagens", "Nossos serviços") em vez de virar fala. '
|
|
137
|
+
+ 'A pauta é para LER e compor com as primitivas — não copie os campos dela para dentro do plano.' },
|
|
123
138
|
],
|
|
124
139
|
},
|
|
125
140
|
{
|
|
@@ -159,13 +174,6 @@ const GRUPOS = [
|
|
|
159
174
|
args: 'todos: lista de {content: a tarefa curta no imperativo, status: pending|in_progress|completed}. Reescreve a lista inteira a cada chamada; só uma "in_progress" por vez.' },
|
|
160
175
|
{ tool: 'pesquisar_referencia', escreve: false, faz: 'lê docs e exemplos antes de escrever — sobe a qualidade',
|
|
161
176
|
args: 'consulta: o que pesquisar, de preferência em inglês. paginas (opcional, número, padrão 3, máximo 5).' },
|
|
162
|
-
// Baixa o arquivo para <projeto>/midia/ — por isso `escreve: true`.
|
|
163
|
-
{ tool: 'buscar_audio', escreve: true, faz: 'acha música ou efeito sonoro livre na internet (Creative Commons), baixa e manda para o estúdio, com o crédito',
|
|
164
|
-
args: 'consulta: o clima ou o som, em inglês ("upbeat corporate", "calm piano", "whoosh", "ding"). tipo (opcional): "musica" (padrão) ou "efeito". '
|
|
165
|
-
+ 'duracao (opcional, segundos): quanto o vídeo dura, para a faixa combinar. Devolve url para trilha.src ou som.arquivo, e o crédito a pôr na peça quando a licença pedir.' },
|
|
166
|
-
{ tool: 'marca_da_empresa', escreve: true, faz: 'puxa do site da empresa a logo, a cor principal, a paleta, as fontes e a frase — pronto para `marca` no estúdio',
|
|
167
|
-
args: 'nome: a empresa como o usuário falou. site (opcional): o endereço, se souber — sem ele, o site é descoberto. '
|
|
168
|
-
+ 'Devolve marca:{cor, logo} para colar no documento, mais cores.paleta, fontes e frase. Chame ANTES de criar uma peça de marca.' },
|
|
169
177
|
// Vai para memory-geral.md, FORA da pasta do projeto — por isso não
|
|
170
178
|
// é write_file/edit_file (a sandbox de resolveInProject bloqueia
|
|
171
179
|
// qualquer caminho fora do projeto, e é assim mesmo que tem que
|
|
@@ -180,14 +188,6 @@ const GRUPOS = [
|
|
|
180
188
|
args: 'pedido: o que o usuário quer, com as palavras dele. pasta (opcional): onde construir — sem ela, uma pasta nova com o nome do pedido. rodadas (opcional, 1 a 4, padrão 2): quantas voltas de correção. '
|
|
181
189
|
+ 'USE quando pedirem um app, um site, uma página ou uma ferramenta INTEIRA, do zero. NÃO use para mexer no que já existe, para um arquivo só, nem para conserto: são seis a oito chamadas de agente, '
|
|
182
190
|
+ 'e gastá-las para trocar a cor de um botão come o dia do usuário. O revisor ABRE a página num navegador invisível e mede — página em branco e erro de JavaScript reprovam sozinhos, o que ele achar volta para o construtor.' },
|
|
183
|
-
{ tool: 'planejar_producao', escreve: false, noLoop: true,
|
|
184
|
-
faz: 'ativa o DIRETOR: roteiro, narração cena a cena, voz, movimento, mídia a buscar e trilha — a estrutura ANTES de produzir',
|
|
185
|
-
args: 'pedido: o que o usuário quer, com as palavras dele. segundos (opcional, padrão 30): duração alvo. '
|
|
186
|
-
+ 'formato (opcional): video | apresentacao. contexto (opcional): o que já se sabe (marca, público, dados).\n'
|
|
187
|
-
+ 'USE SEMPRE antes de criar vídeo, reels, teaser, animação ou apresentação com narração — inclusive quando o pedido parecer simples. '
|
|
188
|
-
+ 'Devolve uma PAUTA: produza exatamente o que ela diz (não invente cena, não troque a ordem, não reescreva a narração), '
|
|
189
|
-
+ 'buscando a mídia com midia_procurar + midia_baixar e montando com studio_criar (corte/prisma). '
|
|
190
|
-
+ 'NÃO use para ajustar peça existente nem quando o usuário pediu só o roteiro em texto.' },
|
|
191
191
|
{ tool: 'finish', escreve: false, faz: 'encerra dizendo o que fez', noLoop: true,
|
|
192
192
|
args: 'summary (opcional): uma frase do que foi feito.' },
|
|
193
193
|
],
|
package/lib/config.js
CHANGED
|
@@ -210,7 +210,7 @@ function appendProjectMemory(dir, entry) {
|
|
|
210
210
|
* vezes numa tarefa só. */
|
|
211
211
|
function lembrarCriacao({ ferramenta, titulo, id }) {
|
|
212
212
|
if (!id) return;
|
|
213
|
-
const linha = `- criou ${ferramenta || 'peça'} "${String(titulo || 'sem título').slice(0, 70)}" —
|
|
213
|
+
const linha = `- criou ${ferramenta || 'peça'} "${String(titulo || 'sem título').slice(0, 70)}" — projeto \`${id}\``;
|
|
214
214
|
try {
|
|
215
215
|
ensureStructure();
|
|
216
216
|
const texto = fs.existsSync(GENERAL_MEMORY) ? fs.readFileSync(GENERAL_MEMORY, 'utf-8') : '';
|
package/lib/conta.js
CHANGED
|
@@ -38,26 +38,6 @@ const CONECTA = process.env.PRIMOCODE_CONTA_SERVER || 'https://conecta-primo-api
|
|
|
38
38
|
|
|
39
39
|
const ARQUIVO = path.join(os.homedir(), '.primocode', 'conta.json');
|
|
40
40
|
|
|
41
|
-
/* ── ONDE O LOGIN ABRE ───────────────────────────────────────────────────
|
|
42
|
-
*
|
|
43
|
-
* "Abre conecta-primo-ai.vercel.app/token do PrimoCode: abre o login normal
|
|
44
|
-
* do app, mas com o token do PrimoCode. Aí o site se comunica com o
|
|
45
|
-
* terminal."
|
|
46
|
-
*
|
|
47
|
-
* A página do token é do SITE, não da API. O servidor devolve a URL dele —
|
|
48
|
-
* que num ambiente antigo pode apontar para outra origem ou para outro
|
|
49
|
-
* caminho — e aqui ela é normalizada para o endereço acima, preservando o que
|
|
50
|
-
* vier na query (é ali que vai o código do pareamento, quando vai).
|
|
51
|
-
* `PRIMOCODE_SITE` troca a origem para rodar contra uma prévia local. */
|
|
52
|
-
const SITE = (process.env.PRIMOCODE_SITE || 'https://conecta-primo-ai.vercel.app').replace(/\/+$/, '');
|
|
53
|
-
const PAGINA_TOKEN = '/token';
|
|
54
|
-
|
|
55
|
-
function paginaDeLogin(urlDoServidor) {
|
|
56
|
-
let query = '';
|
|
57
|
-
try { query = new URL(urlDoServidor).search; } catch { /* sem url utilizável: só a página */ }
|
|
58
|
-
return SITE + PAGINA_TOKEN + query;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
41
|
// Os planos que liberam tudo. A lista é a MESMA do servidor
|
|
62
42
|
// (`PLANOS_COM_PRO`), e quem decide de verdade é ele: isto aqui só evita uma
|
|
63
43
|
// ida à rede para mostrar o selo na abertura.
|
|
@@ -221,11 +201,11 @@ async function entrar({ abrir, contar = () => {}, minutos = 5 } = {}) {
|
|
|
221
201
|
}
|
|
222
202
|
|
|
223
203
|
let origemDoSite = null;
|
|
224
|
-
try { origemDoSite = new URL(
|
|
204
|
+
try { origemDoSite = new URL(pedido.url).origin; } catch { /* url estranha: sem CORS para ela */ }
|
|
225
205
|
const local = await servirPareamento(pedido.codigo, origemDoSite);
|
|
226
206
|
const copiado = await area.copiar(pedido.codigo);
|
|
227
207
|
|
|
228
|
-
let url =
|
|
208
|
+
let url = pedido.url;
|
|
229
209
|
if (local.porta) url += (url.includes('?') ? '&' : '?') + 'porta=' + local.porta;
|
|
230
210
|
|
|
231
211
|
contar({ url, codigo: pedido.codigo, copiado: copiado.ok, porta: local.porta });
|
|
@@ -307,5 +287,4 @@ function versao() {
|
|
|
307
287
|
try { return require('../package.json').version; } catch { return '?'; }
|
|
308
288
|
}
|
|
309
289
|
|
|
310
|
-
module.exports = { entrar, sair, atual, sessao, dispositivos, servirPareamento,
|
|
311
|
-
ARQUIVO, CONECTA, SITE, PAGINA_TOKEN, PLANOS_PRO, PORTA_PAREAMENTO };
|
|
290
|
+
module.exports = { entrar, sair, atual, sessao, dispositivos, servirPareamento, ARQUIVO, CONECTA, PLANOS_PRO, PORTA_PAREAMENTO };
|