primocode 11.1.0 → 11.3.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.
Files changed (47) hide show
  1. package/README.md +16 -2
  2. package/bin/primocode-rpc.js +100 -0
  3. package/lib/agentes-pessoais.js +120 -0
  4. package/lib/app-server/cli.js +64 -0
  5. package/lib/app-server/enderecos.js +43 -0
  6. package/lib/app-server/index.js +3 -0
  7. package/lib/app-server/pareamento.js +61 -0
  8. package/lib/app-server/protocolo.js +86 -0
  9. package/lib/app-server/servidor.js +805 -0
  10. package/lib/app-server/websocket.js +87 -0
  11. package/lib/avisos.js +65 -0
  12. package/lib/cli/sessao.js +202 -17
  13. package/lib/cli-nova.js +237 -0
  14. package/lib/config/config.js +22 -2
  15. package/lib/diff.js +12 -2
  16. package/lib/motor/aprovacao.js +114 -0
  17. package/lib/motor/eventos.js +77 -0
  18. package/lib/motor/index.js +8 -0
  19. package/lib/motor/tela-terminal.js +64 -0
  20. package/lib/motor/transcricao.js +57 -0
  21. package/lib/motor/turno.js +43 -0
  22. package/lib/motor/voz.js +65 -0
  23. package/lib/piloto.js +39 -0
  24. package/lib/plugin/entregaveis/nucleo.js +2 -2
  25. package/lib/plugin/estudio/criar.js +113 -0
  26. package/lib/plugin/estudio/gosto.js +3 -0
  27. package/lib/plugin/estudio/historico.js +60 -0
  28. package/lib/plugin/estudio/index.js +14 -1
  29. package/lib/plugin/estudio/kits/tela.html +3 -1
  30. package/lib/plugin/estudio/miniatura.js +56 -0
  31. package/lib/plugin/video/auto.js +1 -1
  32. package/lib/plugin/video/catalogo.js +20 -0
  33. package/lib/plugin/video/mutar.js +28 -0
  34. package/lib/plugin/video/ops-fx.js +6 -6
  35. package/lib/plugin/video/ops.js +2 -13
  36. package/lib/plugin/video/pedido.js +1 -1
  37. package/lib/plugin/video/zoom.js +1 -1
  38. package/lib/projetos-meta.js +107 -0
  39. package/lib/qr.js +209 -0
  40. package/lib/session/runner/act.js +127 -68
  41. package/lib/skill/catalogo.js +3 -1
  42. package/lib/tool/tools.js +7 -0
  43. package/lib/tui/entrada.js +143 -18
  44. package/lib/tui/tela.js +312 -49
  45. package/lib/tui/ui.js +78 -4
  46. package/package.json +10 -2
  47. package/voz/primo_voz/transcrever.py +12 -0
@@ -0,0 +1,107 @@
1
+ // Projetos do app: pasta em ~/primocode/<projeto>/ + .primocode/projeto.json
2
+ // (nome, descrição, ícone, cor). docs/estrutura.md, seção 7. Node puro.
3
+ // "Apagar" só arquiva (marca no projeto.json): os arquivos da pessoa nunca são removidos.
4
+ 'use strict';
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const ICONES = ['folder', 'film', 'palette', 'workflow', 'pen', 'home', 'shield', 'bell', 'sliders', 'code', 'rocket', 'cake', 'store', 'clapperboard', 'megaphone', 'graduation-cap', 'heart', 'camera', 'music'];
10
+ const COR_PADRAO = '#22d3ee';
11
+ const hexOk = (h) => /^#[0-9a-f]{6}$/i.test(String(h || ''));
12
+ const cfg = () => require('./config/config');
13
+ const raiz = () => cfg().ROOT_DIR;
14
+ const metaDe = (dir) => path.join(dir, '.primocode', 'projeto.json');
15
+
16
+ function lerMeta(dir) { try { return JSON.parse(fs.readFileSync(metaDe(dir), 'utf8')); } catch { return {}; } }
17
+ function gravarMeta(dir, meta) {
18
+ fs.mkdirSync(path.join(dir, '.primocode'), { recursive: true });
19
+ fs.writeFileSync(metaDe(dir), JSON.stringify(meta, null, 2) + '\n');
20
+ }
21
+
22
+ /* id = nome da pasta. Só vale se a pasta existe dentro da raiz. */
23
+ function pastaDe(id) {
24
+ const s = String(id || '');
25
+ if (!/^[\w][\w.-]{0,80}$/.test(s)) return null;
26
+ const dir = path.join(raiz(), s);
27
+ try { return fs.statSync(dir).isDirectory() ? dir : null; } catch { return null; }
28
+ }
29
+
30
+ /* Se a pasta é um repositório git, devolve { git: true, ramo }: lido do .git/HEAD, sem rodar o git. */
31
+ function ramoGit(dir) {
32
+ try {
33
+ const cabeca = fs.readFileSync(path.join(dir, '.git', 'HEAD'), 'utf8').trim();
34
+ const m = /^ref: refs\/heads\/(.+)$/.exec(cabeca);
35
+ return { git: true, ramo: m ? m[1] : cabeca.slice(0, 7) };
36
+ } catch { return { git: false, ramo: '' }; }
37
+ }
38
+
39
+ /* O "dono/repo" do remoto origin, lido do .git/config (sem rodar o git). Vazio se não há. */
40
+ function remotoGit(dir) {
41
+ try {
42
+ const cfg = fs.readFileSync(path.join(dir, '.git', 'config'), 'utf8');
43
+ const m = /\[remote "origin"\][^\[]*?url\s*=\s*(\S+)/.exec(cfg);
44
+ const r = m && /[:/]([\w.-]+\/[\w.-]+?)(?:\.git)?$/.exec(m[1]);
45
+ return r ? r[1] : '';
46
+ } catch { return ''; }
47
+ }
48
+
49
+ /* Quantos itens (arquivos e pastas, sem os ocultos) o projeto tem na raiz, e quando mexeram nele por último. */
50
+ function conteudo(dir) {
51
+ try { return { itens: fs.readdirSync(dir).filter((n) => !n.startsWith('.')).length, atualizado: fs.statSync(dir).mtimeMs }; } catch { return { itens: 0, atualizado: null }; }
52
+ }
53
+
54
+ function resumo(id) {
55
+ const dir = path.join(raiz(), id);
56
+ const m = lerMeta(dir);
57
+ return {
58
+ id, nome: m.nome || id, descricao: m.descricao || '',
59
+ icone: ICONES.includes(m.icone) ? m.icone : 'folder', cor: hexOk(m.cor) ? m.cor : COR_PADRAO,
60
+ caminho: dir, arquivado: !!m.arquivado, criado: m.criado || null,
61
+ ...ramoGit(dir), remoto: remotoGit(dir), ...conteudo(dir),
62
+ };
63
+ }
64
+
65
+ function listar({ arquivados = false } = {}) {
66
+ /* O mais recente primeiro (como a lista da barra lateral); empate por nome. */
67
+ return cfg().listProjects().map(resumo).filter((p) => arquivados || !p.arquivado)
68
+ .sort((a, b) => String(b.criado || '').localeCompare(String(a.criado || '')) || a.nome.localeCompare(b.nome));
69
+ }
70
+
71
+ function limpar(c, { criando = false } = {}) {
72
+ const out = {};
73
+ if ('nome' in c) { out.nome = String(c.nome || '').trim().slice(0, 80); if (!out.nome) throw new Error('falta o nome'); }
74
+ else if (criando) throw new Error('falta o nome');
75
+ if ('descricao' in c) out.descricao = String(c.descricao || '').trim().slice(0, 240);
76
+ if ('icone' in c && c.icone) { if (!ICONES.includes(c.icone)) throw new Error(`ícone inválido (use: ${ICONES.join(', ')})`); out.icone = c.icone; }
77
+ if ('cor' in c && c.cor) { if (!hexOk(c.cor)) throw new Error('cor inválida (use #rrggbb)'); out.cor = String(c.cor).toLowerCase(); }
78
+ return out;
79
+ }
80
+
81
+ function criar(c = {}) {
82
+ const campos = limpar(c, { criando: true });
83
+ const dir = cfg().createProjectDir(campos.nome);
84
+ const id = path.basename(dir);
85
+ const atual = lerMeta(dir);
86
+ if (atual.nome && !atual.arquivado) throw new Error(`já existe o projeto "${atual.nome}"`);
87
+ gravarMeta(dir, { ...atual, ...campos, arquivado: false, criado: atual.criado || new Date().toISOString() });
88
+ return resumo(id);
89
+ }
90
+
91
+ function atualizar(id, c = {}) {
92
+ const dir = pastaDe(id);
93
+ if (!dir) throw new Error(`não achei o projeto ${id}`);
94
+ const campos = limpar(c);
95
+ if (!Object.keys(campos).length) throw new Error('nada para mudar (nome, descricao, icone, cor)');
96
+ gravarMeta(dir, { ...lerMeta(dir), ...campos });
97
+ return resumo(path.basename(dir));
98
+ }
99
+
100
+ function arquivar(id) {
101
+ const dir = pastaDe(id);
102
+ if (!dir) throw new Error(`não achei o projeto ${id}`);
103
+ gravarMeta(dir, { ...lerMeta(dir), arquivado: true });
104
+ return { id: path.basename(dir), arquivado: true, arquivos: 'mantidos' };
105
+ }
106
+
107
+ module.exports = { ICONES, listar, criar, atualizar, arquivar, pastaDe, resumo };
package/lib/qr.js ADDED
@@ -0,0 +1,209 @@
1
+ // Gerador de QR code mínimo, sem dependência: modo byte (UTF-8), correção L ou M, versões 1 a 10
2
+ // (até 213 bytes no M, 271 no L). Serve para o pareamento do celular (docs/estrutura.md, seção 15).
3
+ // Algoritmo da ISO/IEC 18004: Reed-Solomon em GF(256), 8 máscaras, escolhe a de menor penalidade.
4
+ 'use strict';
5
+
6
+ const ECC_POR_BLOCO = { L: [7, 10, 15, 20, 26, 18, 20, 24, 30, 18], M: [10, 16, 26, 18, 24, 16, 18, 22, 22, 26] };
7
+ const NUM_BLOCOS = { L: [1, 1, 1, 1, 1, 2, 2, 2, 2, 4], M: [1, 1, 1, 2, 2, 4, 4, 4, 5, 5] };
8
+ const FORMATO_ECC = { L: 1, M: 0 };
9
+ const VERSAO_MAX = 10;
10
+
11
+ /* Módulos que sobram para dados + correção, descontados padrões de função. */
12
+ function modulosBrutos(v) {
13
+ let r = (16 * v + 128) * v + 64;
14
+ if (v >= 2) {
15
+ const n = Math.floor(v / 7) + 2;
16
+ r -= (25 * n - 10) * n - 55;
17
+ if (v >= 7) r -= 36;
18
+ }
19
+ return r;
20
+ }
21
+ const dadosCodewords = (v, ecc) => Math.floor(modulosBrutos(v) / 8) - ECC_POR_BLOCO[ecc][v - 1] * NUM_BLOCOS[ecc][v - 1];
22
+
23
+ /* GF(256) com o polinômio 0x11D. */
24
+ function multiplicar(x, y) {
25
+ let z = 0;
26
+ for (let i = 7; i >= 0; i--) { z = (z << 1) ^ ((z >>> 7) * 0x11D); z ^= ((y >>> i) & 1) * x; }
27
+ return z;
28
+ }
29
+ function divisor(grau) {
30
+ const r = new Array(grau).fill(0);
31
+ r[grau - 1] = 1;
32
+ let raiz = 1;
33
+ for (let i = 0; i < grau; i++) {
34
+ for (let j = 0; j < r.length; j++) { r[j] = multiplicar(r[j], raiz); if (j + 1 < r.length) r[j] ^= r[j + 1]; }
35
+ raiz = multiplicar(raiz, 2);
36
+ }
37
+ return r;
38
+ }
39
+ function resto(dados, div) {
40
+ const r = new Array(div.length).fill(0);
41
+ for (const b of dados) {
42
+ const f = b ^ r.shift();
43
+ r.push(0);
44
+ div.forEach((c, i) => { r[i] ^= multiplicar(c, f); });
45
+ }
46
+ return r;
47
+ }
48
+
49
+ function bitsDoTexto(bytes, v, ecc) {
50
+ const bits = [];
51
+ const poe = (valor, n) => { for (let i = n - 1; i >= 0; i--) bits.push((valor >>> i) & 1); };
52
+ poe(0b0100, 4);
53
+ poe(bytes.length, v <= 9 ? 8 : 16);
54
+ for (const b of bytes) poe(b, 8);
55
+ const cap = dadosCodewords(v, ecc) * 8;
56
+ poe(0, Math.min(4, cap - bits.length));
57
+ poe(0, (8 - (bits.length % 8)) % 8);
58
+ for (let pad = 0xEC; bits.length < cap; pad ^= 0xEC ^ 0x11) poe(pad, 8);
59
+ const cw = [];
60
+ for (let i = 0; i < bits.length; i += 8) cw.push(parseInt(bits.slice(i, i + 8).join(''), 2));
61
+ return cw;
62
+ }
63
+
64
+ /* Divide em blocos, calcula a correção e intercala. */
65
+ function intercalar(dados, v, ecc) {
66
+ const nb = NUM_BLOCOS[ecc][v - 1];
67
+ const eb = ECC_POR_BLOCO[ecc][v - 1];
68
+ const total = Math.floor(modulosBrutos(v) / 8);
69
+ const curtos = nb - (total % nb);
70
+ const lenCurto = Math.floor(total / nb) - eb;
71
+ const div = divisor(eb);
72
+ const blocos = [];
73
+ for (let i = 0, k = 0; i < nb; i++) {
74
+ const dat = dados.slice(k, k + lenCurto + (i < curtos ? 0 : 1));
75
+ k += dat.length;
76
+ blocos.push({ dat, ecc: resto(dat, div) });
77
+ }
78
+ const out = [];
79
+ for (let i = 0; i <= lenCurto; i++) for (const b of blocos) if (i < b.dat.length) out.push(b.dat[i]);
80
+ for (let i = 0; i < eb; i++) for (const b of blocos) out.push(b.ecc[i]);
81
+ return out;
82
+ }
83
+
84
+ function criarMatriz(v) {
85
+ const n = v * 4 + 17;
86
+ return { n, m: Array.from({ length: n }, () => new Array(n).fill(false)), f: Array.from({ length: n }, () => new Array(n).fill(false)) };
87
+ }
88
+ function posAlinhamento(v) {
89
+ if (v === 1) return [];
90
+ const n = Math.floor(v / 7) + 2;
91
+ const passo = v === 32 ? 26 : Math.ceil((v * 4 + 4) / (n * 2 - 2)) * 2;
92
+ const r = [6];
93
+ for (let p = v * 4 + 10; r.length < n; p -= passo) r.splice(1, 0, p);
94
+ return r;
95
+ }
96
+ function desenharFuncoes(Q, v, ecc) {
97
+ const { n, m, f } = Q;
98
+ const set = (x, y, cor) => { if (x >= 0 && x < n && y >= 0 && y < n) { m[y][x] = cor; f[y][x] = true; } };
99
+ for (let i = 0; i < n; i++) { set(6, i, i % 2 === 0); set(i, 6, i % 2 === 0); }
100
+ const busca = (cx, cy) => { for (let dy = -4; dy <= 4; dy++) for (let dx = -4; dx <= 4; dx++) { const d = Math.max(Math.abs(dx), Math.abs(dy)); set(cx + dx, cy + dy, d !== 2 && d !== 4); } };
101
+ busca(3, 3); busca(n - 4, 3); busca(3, n - 4);
102
+ const al = posAlinhamento(v);
103
+ for (let i = 0; i < al.length; i++) for (let j = 0; j < al.length; j++) {
104
+ if ((i === 0 && j === 0) || (i === 0 && j === al.length - 1) || (i === al.length - 1 && j === 0)) continue;
105
+ for (let dy = -2; dy <= 2; dy++) for (let dx = -2; dx <= 2; dx++) set(al[i] + dx, al[j] + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);
106
+ }
107
+ desenharFormato(Q, ecc, 0);
108
+ if (v >= 7) {
109
+ let r = v;
110
+ for (let i = 0; i < 12; i++) r = (r << 1) ^ ((r >>> 11) * 0x1F25);
111
+ const bits = (v << 12) | r;
112
+ for (let i = 0; i < 18; i++) {
113
+ const cor = ((bits >>> i) & 1) !== 0;
114
+ const a = n - 11 + (i % 3);
115
+ const b = Math.floor(i / 3);
116
+ set(a, b, cor); set(b, a, cor);
117
+ }
118
+ }
119
+ }
120
+ function desenharFormato(Q, ecc, mascara) {
121
+ const { n, m, f } = Q;
122
+ const dados = (FORMATO_ECC[ecc] << 3) | mascara;
123
+ let r = dados;
124
+ for (let i = 0; i < 10; i++) r = (r << 1) ^ ((r >>> 9) * 0x537);
125
+ const bits = ((dados << 10) | r) ^ 0x5412;
126
+ const bit = (i) => ((bits >>> i) & 1) !== 0;
127
+ const set = (x, y, cor) => { m[y][x] = cor; f[y][x] = true; };
128
+ for (let i = 0; i <= 5; i++) set(8, i, bit(i));
129
+ set(8, 7, bit(6)); set(8, 8, bit(7)); set(7, 8, bit(8));
130
+ for (let i = 9; i < 15; i++) set(14 - i, 8, bit(i));
131
+ for (let i = 0; i < 8; i++) set(n - 1 - i, 8, bit(i));
132
+ for (let i = 8; i < 15; i++) set(8, n - 15 + i, bit(i));
133
+ set(8, n - 8, true);
134
+ }
135
+ function colocarDados(Q, cw) {
136
+ const { n, m, f } = Q;
137
+ let i = 0;
138
+ for (let direita = n - 1; direita >= 1; direita -= 2) {
139
+ if (direita === 6) direita = 5;
140
+ for (let vert = 0; vert < n; vert++) for (let j = 0; j < 2; j++) {
141
+ const x = direita - j;
142
+ const y = ((direita + 1) & 2) === 0 ? n - 1 - vert : vert;
143
+ if (!f[y][x] && i < cw.length * 8) { m[y][x] = ((cw[i >>> 3] >>> (7 - (i & 7))) & 1) !== 0; i++; }
144
+ }
145
+ }
146
+ }
147
+ const MASCARAS = [
148
+ (x, y) => (x + y) % 2 === 0, (x, y) => y % 2 === 0, (x) => x % 3 === 0, (x, y) => (x + y) % 3 === 0,
149
+ (x, y) => (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0, (x, y) => ((x * y) % 2) + ((x * y) % 3) === 0,
150
+ (x, y) => (((x * y) % 2) + ((x * y) % 3)) % 2 === 0, (x, y) => (((x + y) % 2) + ((x * y) % 3)) % 2 === 0,
151
+ ];
152
+ function aplicarMascara(Q, k) {
153
+ for (let y = 0; y < Q.n; y++) for (let x = 0; x < Q.n; x++) if (!Q.f[y][x] && MASCARAS[k](x, y)) Q.m[y][x] = !Q.m[y][x];
154
+ }
155
+ function penalidade(Q) {
156
+ const { n, m } = Q;
157
+ let p = 0;
158
+ const corrida = (linha) => {
159
+ let s = 0; let run = 1;
160
+ for (let i = 1; i <= linha.length; i++) {
161
+ if (i < linha.length && linha[i] === linha[i - 1]) run++;
162
+ else { if (run >= 5) s += run - 2; run = 1; }
163
+ }
164
+ const str = linha.map((b) => (b ? 1 : 0)).join('');
165
+ for (const pad of ['10111010000', '00001011101']) { let at = str.indexOf(pad); while (at >= 0) { s += 40; at = str.indexOf(pad, at + 1); } }
166
+ return s;
167
+ };
168
+ for (let y = 0; y < n; y++) p += corrida(m[y]);
169
+ for (let x = 0; x < n; x++) p += corrida(m.map((l) => l[x]));
170
+ for (let y = 0; y < n - 1; y++) for (let x = 0; x < n - 1; x++) if (m[y][x] === m[y][x + 1] && m[y][x] === m[y + 1][x] && m[y][x] === m[y + 1][x + 1]) p += 3;
171
+ const escuros = m.reduce((a, l) => a + l.filter(Boolean).length, 0);
172
+ p += (Math.ceil(Math.abs(escuros * 20 - n * n * 10) / (n * n)) - 1) * 10;
173
+ return p;
174
+ }
175
+
176
+ /* Devolve { tamanho, modulos: boolean[][], versao }. Lança se o texto não cabe. */
177
+ function gerarQR(texto, { ecc = 'M' } = {}) {
178
+ if (!ECC_POR_BLOCO[ecc]) throw new Error('ecc deve ser L ou M');
179
+ const bytes = Buffer.from(String(texto), 'utf8');
180
+ let v = 1;
181
+ for (; v <= VERSAO_MAX; v++) {
182
+ const bitsLen = 4 + (v <= 9 ? 8 : 16) + bytes.length * 8;
183
+ if (bitsLen <= dadosCodewords(v, ecc) * 8) break;
184
+ }
185
+ if (v > VERSAO_MAX) throw new Error(`texto grande demais para o QR (${bytes.length} bytes; máximo ${dadosCodewords(VERSAO_MAX, ecc) - 3})`);
186
+ const cw = intercalar(bitsDoTexto(bytes, v, ecc), v, ecc);
187
+ let melhor = null;
188
+ for (let k = 0; k < 8; k++) {
189
+ const Q = criarMatriz(v);
190
+ desenharFuncoes(Q, v, ecc);
191
+ colocarDados(Q, cw);
192
+ aplicarMascara(Q, k);
193
+ desenharFormato(Q, ecc, k);
194
+ const pen = penalidade(Q);
195
+ if (!melhor || pen < melhor.pen) melhor = { pen, Q };
196
+ }
197
+ return { tamanho: melhor.Q.n, modulos: melhor.Q.m, versao: v };
198
+ }
199
+
200
+ /* SVG pronto (módulos escuros em `cor`, fundo em `fundo`, com a zona de silêncio de 4 módulos). */
201
+ function qrSvg(texto, { ecc = 'M', cor = '#000', fundo = '#fff' } = {}) {
202
+ const { tamanho, modulos } = gerarQR(texto, { ecc });
203
+ const t = tamanho + 8;
204
+ let d = '';
205
+ for (let y = 0; y < tamanho; y++) for (let x = 0; x < tamanho; x++) if (modulos[y][x]) d += `M${x + 4} ${y + 4}h1v1h-1z`;
206
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${t} ${t}" shape-rendering="crispEdges" role="img" aria-label="QR code de pareamento"><rect width="${t}" height="${t}" fill="${fundo}"/><path d="${d}" fill="${cor}"/></svg>`;
207
+ }
208
+
209
+ module.exports = { gerarQR, qrSvg };