@getparallel/parx 0.4.4 → 0.6.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/dist/config.js CHANGED
@@ -96,6 +96,10 @@ export function leerAjustes(dir = carpetaDeConfiguracion()) {
96
96
  out.raton = a.raton;
97
97
  if (typeof a.animacion === 'boolean')
98
98
  out.animacion = a.animacion;
99
+ if (typeof a.autoactualizar === 'boolean')
100
+ out.autoactualizar = a.autoactualizar;
101
+ if (typeof a.remoto === 'boolean')
102
+ out.remoto = a.remoto;
99
103
  const k = a.consejos;
100
104
  if (k && typeof k === 'object') {
101
105
  const n = (x) => (typeof x === 'number' && Number.isFinite(x) && x >= 0 ? Math.floor(x) : 0);
package/dist/consejos.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { capacidades } from "./marca.js";
2
2
  import { t } from "./textos.js";
3
- export const CONSEJOS = ['menu', 'modo', 'una', 'bash', 'imagen', 'historial', 'pegar', 'deshacer', 'modelo', 'carriles', 'parar', 'cola', 'bandeja', 'raton', 'informe', 'mision', 'ensenar', 'saldo', 'cuenta', 'agents'];
3
+ export const CONSEJOS = ['menu', 'modo', 'una', 'bash', 'imagen', 'historial', 'pegar', 'deshacer', 'modelo', 'carriles', 'parar', 'cola', 'bandeja', 'raton', 'informe', 'mision', 'ensenar', 'saldo', 'cuenta', 'agents', 'arroba'];
4
4
  export function consejo(id) {
5
5
  const u = capacidades().unicode;
6
6
  const tecla = {
@@ -24,6 +24,7 @@ export function consejo(id) {
24
24
  bash: '!',
25
25
  bandeja: '/inbox',
26
26
  raton: 'Click',
27
+ arroba: '@',
27
28
  };
28
29
  return { tecla: tecla[id], texto: t(`tip.${id}`) };
29
30
  }
@@ -0,0 +1,118 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { carpetaDeConfiguracion } from "./config.js";
4
+ import { MODELOS, ORDEN_DE_MODELOS, nombreDeModelo } from "./modelos.js";
5
+ import { t } from "./textos.js";
6
+ export const PERFILES = {
7
+ pregunta: { vueltas: 2, crecimiento: 700, salidaPorVuelta: 80, salidaFinal: 350 },
8
+ pequena: { vueltas: 4, crecimiento: 900, salidaPorVuelta: 350, salidaFinal: 200 },
9
+ media: { vueltas: 8, crecimiento: 1200, salidaPorVuelta: 550, salidaFinal: 250 },
10
+ grande: { vueltas: 16, crecimiento: 1500, salidaPorVuelta: 650, salidaFinal: 350 },
11
+ };
12
+ const GRANDE = /\b(refactor|refactoriz|migrat|migra[r]?\b|rewrite|reescrib|rework|whole (project|app|codebase|repo)|entire|todo el (proyecto|código|repo)|toda la (app|aplicación)|all (the )?files|todos los ficheros|architecture|arquitectura|from scratch|desde cero|full[- ]stack|authentication|autenticaci|database|base de datos|deploy|despliega|integrat|integra[r]?\b|test suite|end[- ]to[- ]end|e2e|multi[- ]?page|varias páginas|dashboard|backend|api rest|microservic)/i;
13
+ const CREA = /\b(create|build|make|write|generate|implement|crea[r]?|construye|monta|escribe|genera|implementa|haz(me)?|new|nuev[oa])\b/i;
14
+ const SUSTANCIAL = /\b(page|página|pagina|website|web ?site|site|web|app|form|formulario|component|componente|feature|funcionalidad|endpoint|screen|pantalla|module|módulo|modulo|api|script|server|servidor|service|servicio|table|tabla|model|modelo|view|vista)\b/i;
15
+ const PEQUENA = /\b(typo|errata|rename|renombra|one[- ]line|una línea|small|pequeñ|minor|mínim|quick|rápid|simple|sencill|tiny|just|solo|sólo|only|fix the|arregla el|arregla la|change the|cambia el|cambia la|add a |añade un|añade una|remove the|quita el|quita la|update the|actualiza el)\b/i;
16
+ const PREGUNTA = /^(what|why|how|where|which|who|when|is|are|does|do|can|could|should|explain|describe|tell me|show me|list|qué|que|por qué|cómo|como|dónde|donde|cuál|cual|quién|quien|cuándo|explica|describe|dime|enséñame|lista|hay)\b/i;
17
+ function palabras(s) {
18
+ return s.trim().split(/\s+/).filter(Boolean).length;
19
+ }
20
+ export function clasificarTarea(pedido) {
21
+ const s = pedido.replace(/\s+/g, ' ').trim();
22
+ const n = palabras(s);
23
+ if (!s)
24
+ return 'pequena';
25
+ if (GRANDE.test(s) || n > 90)
26
+ return 'grande';
27
+ const pregunta = PREGUNTA.test(s) || (s.endsWith('?') && !CREA.test(s));
28
+ if (pregunta && !CREA.test(s))
29
+ return 'pregunta';
30
+ const creaAlgo = CREA.test(s) || SUSTANCIAL.test(s);
31
+ if (n <= 10 && !creaAlgo)
32
+ return 'pequena';
33
+ if (PEQUENA.test(s) && !creaAlgo)
34
+ return 'pequena';
35
+ return 'media';
36
+ }
37
+ export function tokensDe(texto) {
38
+ const chars = typeof texto === 'number' ? texto : texto.length;
39
+ return Math.ceil(chars / 3.8);
40
+ }
41
+ export function accionesEnBruto(clase, baseTokens, tarifa) {
42
+ const p = PERFILES[clase];
43
+ let entrada = 0;
44
+ let salida = 0;
45
+ for (let i = 0; i < p.vueltas; i++) {
46
+ entrada += baseTokens + i * p.crecimiento;
47
+ salida += i === p.vueltas - 1 ? p.salidaFinal : p.salidaPorVuelta;
48
+ }
49
+ return (entrada * tarifa.entrada + salida * tarifa.salida) / 1000;
50
+ }
51
+ export function redondear(n) {
52
+ if (n < 1)
53
+ return 1;
54
+ if (n < 10)
55
+ return Math.round(n);
56
+ const magnitud = 10 ** (Math.floor(Math.log10(n)) - 1);
57
+ return Math.round(n / magnitud) * magnitud;
58
+ }
59
+ export function estimarTarea(e) {
60
+ const clase = clasificarTarea(e.pedido);
61
+ const base = tokensDe(e.contextoChars) + tokensDe(e.pedido);
62
+ const acciones = {};
63
+ for (const m of ORDEN_DE_MODELOS) {
64
+ const bruto = accionesEnBruto(clase, base, e.tarifas[MODELOS[m].servidor]);
65
+ acciones[m] = redondear(bruto * factorDeCalibracion(e.calibracion, clase, m));
66
+ }
67
+ return { clase, acciones };
68
+ }
69
+ export function lineaDeEstimacion(est, modelo) {
70
+ const actual = est.acciones[modelo];
71
+ let sugerido = null;
72
+ if (modelo === 'summit' && est.clase !== 'grande')
73
+ sugerido = 'stride';
74
+ if ((modelo === 'summit' || modelo === 'stride') && (est.clase === 'pregunta' || est.clase === 'pequena'))
75
+ sugerido = 'swift';
76
+ if (sugerido && est.acciones[sugerido] <= actual * 0.6 && actual >= 5) {
77
+ return t('estimacion.con', { n: actual.toLocaleString('en-US'), modelo: nombreDeModelo(sugerido).replace(/ \d+(\.\d+)?$/, ''), otro: est.acciones[sugerido].toLocaleString('en-US') });
78
+ }
79
+ return t('estimacion.linea', { n: actual.toLocaleString('en-US') });
80
+ }
81
+ const FICHERO = 'estimaciones.json';
82
+ const PESO = 0.3;
83
+ export function leerCalibracion(dir = carpetaDeConfiguracion()) {
84
+ try {
85
+ const c = JSON.parse(readFileSync(join(dir, FICHERO), 'utf8'));
86
+ return c && typeof c === 'object' ? c : {};
87
+ }
88
+ catch {
89
+ return {};
90
+ }
91
+ }
92
+ function guardarCalibracion(c, dir) {
93
+ try {
94
+ if (!existsSync(dir))
95
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
96
+ writeFileSync(join(dir, FICHERO), JSON.stringify(c, null, 2), { mode: 0o600 });
97
+ }
98
+ catch {
99
+ }
100
+ }
101
+ export function factorDeCalibracion(c, clase, modelo) {
102
+ const f = c?.[clase]?.[modelo]?.factor;
103
+ if (typeof f !== 'number' || !Number.isFinite(f) || f <= 0)
104
+ return 1;
105
+ return Math.min(2, Math.max(0.5, f));
106
+ }
107
+ export function aprenderDeLaTarea(o) {
108
+ const dir = o.dir ?? carpetaDeConfiguracion();
109
+ const c = o.calibracion ?? leerCalibracion(dir);
110
+ if (!(o.estimada > 0) || !(o.real > 0))
111
+ return c;
112
+ const ratio = Math.min(4, Math.max(0.25, o.real / o.estimada));
113
+ const antes = c[o.clase]?.[o.modelo];
114
+ const factor = antes ? antes.factor * (1 - PESO) + ratio * PESO : ratio;
115
+ c[o.clase] = { ...(c[o.clase] ?? {}), [o.modelo]: { factor: Number(factor.toFixed(3)), n: (antes?.n ?? 0) + 1 } };
116
+ guardarCalibracion(c, dir);
117
+ return c;
118
+ }
package/dist/firmas.js ADDED
@@ -0,0 +1,55 @@
1
+ import { createHash, createVerify } from 'node:crypto';
2
+ export const CLAVES_DE_NPM = [
3
+ {
4
+ keyid: 'SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U',
5
+ key: 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==',
6
+ },
7
+ ];
8
+ const REGISTRO_DE_NPM = 'https://registry.npmjs.org';
9
+ export function keyidDe(spkiBase64) {
10
+ return `SHA256:${createHash('sha256').update(Buffer.from(spkiBase64, 'base64')).digest('base64').replace(/=+$/, '')}`;
11
+ }
12
+ export function clavesDeConfianza(env = process.env) {
13
+ const claves = [...CLAVES_DE_NPM];
14
+ const registro = (env.PARX_REGISTRY ?? '').replace(/\/+$/, '');
15
+ if (registro && registro !== REGISTRO_DE_NPM && env.PARX_CLAVES_DE_FIRMA) {
16
+ for (const k of env.PARX_CLAVES_DE_FIRMA.split(',').map((x) => x.trim()).filter(Boolean))
17
+ claves.push({ keyid: keyidDe(k), key: k });
18
+ }
19
+ return claves;
20
+ }
21
+ export function mensajeFirmado(nombre, version, integrity) {
22
+ return `${nombre}@${version}:${integrity}`;
23
+ }
24
+ export function firmaValida(d, claves = CLAVES_DE_NPM) {
25
+ if (!Array.isArray(d.firmas) || !d.firmas.length)
26
+ return false;
27
+ if (!/^sha512-[A-Za-z0-9+/]+={0,2}$/.test(d.integrity))
28
+ return false;
29
+ const mensaje = mensajeFirmado(d.nombre, d.version, d.integrity);
30
+ for (const f of d.firmas) {
31
+ if (!f || typeof f !== 'object')
32
+ continue;
33
+ const { keyid, sig } = f;
34
+ if (typeof keyid !== 'string' || typeof sig !== 'string')
35
+ continue;
36
+ const clave = claves.find((k) => k.keyid === keyid);
37
+ if (!clave)
38
+ continue;
39
+ try {
40
+ const v = createVerify('SHA256');
41
+ v.update(mensaje);
42
+ if (v.verify(`-----BEGIN PUBLIC KEY-----\n${clave.key}\n-----END PUBLIC KEY-----`, sig, 'base64'))
43
+ return true;
44
+ }
45
+ catch {
46
+ }
47
+ }
48
+ return false;
49
+ }
50
+ export function integridadDe(contenido) {
51
+ return `sha512-${createHash('sha512').update(contenido).digest('base64')}`;
52
+ }
53
+ export function integridadCoincide(contenido, integrity) {
54
+ return integridadDe(contenido) === integrity;
55
+ }
package/dist/marca.js CHANGED
@@ -125,27 +125,64 @@ export function logo() {
125
125
  const tonos = [c.marca, c.claro, c.claro, c.violeta];
126
126
  return filas.map((f, i) => tonos[i](f));
127
127
  }
128
- export const ANCHO_DE_PAR = 6;
129
- export function cabezaDePar() {
130
- return caps.unicode ? c.claro('▗▄▄▄▄▖') : c.claro(' .--. ');
128
+ export const ANCHO_DE_PAR = 8;
129
+ function miniPar(gesto) {
130
+ const ojo = gesto === 'error' ? 'R' : 'E';
131
+ const fila1 = ['.', '.', 'B', 'B', '.', 'B', 'B', '.'];
132
+ if (gesto === 'pregunta') {
133
+ fila1.splice(0, 8, '.', '.', ojo, ojo, '.', ojo, ojo, '.');
134
+ }
135
+ else if (gesto !== 'parpadeo') {
136
+ const izquierda = gesto === 1 || gesto === 2;
137
+ const a = izquierda ? 2 : 3;
138
+ fila1[a] = ojo;
139
+ fila1[a + 3] = ojo;
140
+ }
141
+ return [
142
+ ['.', '.', '.', 'B', 'B', '.', 'B', 'B'],
143
+ fila1,
144
+ ['.', 'B', 'B', '.', 'B', 'B', '.', '.'],
145
+ ['B', 'B', '.', 'B', 'B', '.', '.', '.'],
146
+ ];
131
147
  }
132
- export function caraDePar(gesto = 'quieto') {
133
- const u = caps.unicode;
134
- const ojos = gesto === 'parpadeo'
135
- ? u ? ' ── ' : ' -- '
136
- : gesto === 'pregunta'
137
- ? u ? ' •• ' : ' oo '
138
- : gesto === 'error'
139
- ? u ? ' ×× ' : ' xx '
140
- : typeof gesto === 'number'
141
- ?
142
- (u ? ['╱╱ ', ' ╱╱ ', ' ╱╱', ' ╱╱ '] : ['// ', ' // ', ' //', ' // '])[gesto]
143
- : u ? ' ╱╱ ' : ' // ';
144
- if (caps.color) {
145
- const fondo = caps.color === 3 ? '\x1b[48;2;139;112;255m' : caps.color === 2 ? '\x1b[48;5;141m' : '\x1b[45m';
146
- return `${fondo}\x1b[1;97m ${ojos} \x1b[22;39;49m`;
148
+ const RGB_DE_PAR = { B: [139, 112, 255], E: [246, 243, 255], R: [255, 107, 107] };
149
+ const C256_DE_PAR = { B: 141, E: 255, R: 203 };
150
+ function filaDePar(arriba, abajo) {
151
+ let out = '';
152
+ for (let x = 0; x < ANCHO_DE_PAR; x++) {
153
+ const a = arriba[x];
154
+ const b = abajo[x];
155
+ if (!caps.unicode || !caps.color) {
156
+ const hay = a !== '.' || b !== '.';
157
+ const ojo = a === 'E' || a === 'R' || b === 'E' || b === 'R';
158
+ out += ojo ? 'o' : hay ? (caps.unicode ? '█' : '/') : ' ';
159
+ continue;
160
+ }
161
+ if (a === '.' && b === '.') {
162
+ out += ' ';
163
+ continue;
164
+ }
165
+ const fg = a === '.' ? null : a;
166
+ const bg = b === '.' ? null : b;
167
+ const color = (p, fondo) => caps.color === 3 ? `\x1b[${fondo ? 48 : 38};2;${RGB_DE_PAR[p].join(';')}m` : caps.color === 2 ? `\x1b[${fondo ? 48 : 38};5;${C256_DE_PAR[p]}m` : p === 'B' ? `\x1b[${fondo ? 45 : 35}m` : p === 'R' ? `\x1b[${fondo ? 41 : 31}m` : `\x1b[${fondo ? 47 : 97}m`;
168
+ if (fg && bg && fg === bg)
169
+ out += `${color(fg, false)}█\x1b[39m`;
170
+ else if (fg && bg)
171
+ out += `${color(fg, false)}${color(bg, true)}▀\x1b[39;49m`;
172
+ else if (fg)
173
+ out += `${color(fg, false)}▀\x1b[39m`;
174
+ else
175
+ out += `${color(bg, false)}▄\x1b[39m`;
147
176
  }
148
- return u ? `▐${ojos}▌` : `[${ojos}]`;
177
+ return out;
178
+ }
179
+ export function cabezaDePar(gesto = 'quieto') {
180
+ const p = miniPar(gesto);
181
+ return filaDePar(p[0], p[1]);
182
+ }
183
+ export function caraDePar(gesto = 'quieto') {
184
+ const p = miniPar(gesto);
185
+ return filaDePar(p[2], p[3]);
149
186
  }
150
187
  export const ANCHO_DE_ANIMACION = 8;
151
188
  export function animacion(tipo, paso) {
@@ -0,0 +1,85 @@
1
+ export const LARGO_MAXIMO_CONSULTA = 30;
2
+ export function normalizar(texto) {
3
+ return texto.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
4
+ }
5
+ export function detectarArroba(texto, cursor) {
6
+ if (cursor < 1 || cursor > texto.length)
7
+ return null;
8
+ const antes = texto.slice(0, cursor);
9
+ const arroba = antes.lastIndexOf('@');
10
+ if (arroba < 0)
11
+ return null;
12
+ const previo = arroba === 0 ? '' : antes[arroba - 1];
13
+ if (previo !== '' && !/\s/.test(previo))
14
+ return null;
15
+ const consulta = antes.slice(arroba + 1);
16
+ if (consulta.length > LARGO_MAXIMO_CONSULTA)
17
+ return null;
18
+ if (/\n|\s\s|^\s/.test(consulta))
19
+ return null;
20
+ return { inicio: arroba, consulta };
21
+ }
22
+ export function mencionTerminada(consulta, conexiones) {
23
+ if (!/\s$/.test(consulta))
24
+ return false;
25
+ const q = normalizar(consulta.trim());
26
+ return conexiones.some((c) => normalizar(c.nombre) === q || normalizar(c.provider) === q);
27
+ }
28
+ export function filtrarConexiones(conexiones, consulta) {
29
+ return puntuar(conexiones, consulta)
30
+ .sort((a, b) => a.p - b.p || a.c.nombre.localeCompare(b.c.nombre))
31
+ .map((x) => x.c);
32
+ }
33
+ function puntuar(conexiones, consulta) {
34
+ const q = normalizar(consulta.trim());
35
+ const puntuados = [];
36
+ for (const c of conexiones) {
37
+ const nombre = normalizar(c.nombre);
38
+ const clave = normalizar(c.provider);
39
+ let p = -1;
40
+ if (!q || nombre.startsWith(q) || clave.startsWith(q))
41
+ p = 0;
42
+ else if (nombre.split(/[\s._-]+/).some((w) => w.startsWith(q)) || clave.split('_').some((w) => w.startsWith(q)))
43
+ p = 1;
44
+ else if (nombre.includes(q) || clave.includes(q))
45
+ p = 2;
46
+ if (p >= 0)
47
+ puntuados.push({ c, p });
48
+ }
49
+ return puntuados;
50
+ }
51
+ export function conexionesQueSalen(conexiones, consulta, max = 40) {
52
+ return puntuar(conexiones, consulta)
53
+ .sort((a, b) => a.p - b.p || Number(b.c.conectada) - Number(a.c.conectada) || a.c.nombre.localeCompare(b.c.nombre))
54
+ .slice(0, max)
55
+ .map((x) => x.c);
56
+ }
57
+ export function mencionEnLaCaja(unidades, cursor) {
58
+ if (unidades[0] === '!')
59
+ return null;
60
+ const antes = unidades.slice(0, Math.max(0, Math.min(cursor, unidades.length)));
61
+ let arroba = -1;
62
+ for (let i = antes.length - 1; i >= 0; i--) {
63
+ const u = antes[i];
64
+ if (u === '@') {
65
+ arroba = i;
66
+ break;
67
+ }
68
+ if (u.length !== 1 || antes.length - i > LARGO_MAXIMO_CONSULTA + 1)
69
+ return null;
70
+ }
71
+ if (arroba < 0)
72
+ return null;
73
+ const previo = arroba === 0 ? '' : antes[arroba - 1];
74
+ if (previo !== '' && (previo.length !== 1 || !/\s/.test(previo)))
75
+ return null;
76
+ const consulta = antes.slice(arroba + 1).join('');
77
+ return detectarArroba(`@${consulta}`, consulta.length + 1) ? { inicio: arroba, consulta } : null;
78
+ }
79
+ export function insertarMencion(unidades, inicio, cursor, nombre) {
80
+ const despues = unidades.slice(cursor);
81
+ const separador = despues[0] === ' ' ? [] : [' '];
82
+ const etiqueta = [...Array.from(`@${nombre}`), ...separador];
83
+ const nuevas = [...unidades.slice(0, inicio), ...etiqueta, ...despues];
84
+ return { unidades: nuevas, cursor: inicio + etiqueta.length + (separador.length ? 0 : 1) };
85
+ }