@getparallel/parx 0.0.0-stage → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/LICENSE +12 -0
- package/README.md +138 -2
- package/dist/actualizar.js +112 -0
- package/dist/agente.js +231 -0
- package/dist/api.js +95 -0
- package/dist/carriles.js +44 -0
- package/dist/cli.js +700 -0
- package/dist/config.js +112 -0
- package/dist/consejos.js +54 -0
- package/dist/cuenta.js +261 -0
- package/dist/diff.js +80 -0
- package/dist/herramientas.js +498 -0
- package/dist/imagenes.js +94 -0
- package/dist/informe.js +65 -0
- package/dist/marca.js +200 -0
- package/dist/mision.js +16 -0
- package/dist/modelos.js +28 -0
- package/dist/notificar.js +112 -0
- package/dist/qr.js +272 -0
- package/dist/registro.js +24 -0
- package/dist/seguridad.js +164 -0
- package/dist/shell.js +108 -0
- package/dist/texto.js +165 -0
- package/dist/textos.js +273 -0
- package/dist/tui.js +1198 -0
- package/dist/ui.js +287 -0
- package/package.json +60 -4
package/dist/config.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
export function carpetaDeConfiguracion(env = process.env, plataforma = process.platform) {
|
|
5
|
+
if (env.PARX_HOME)
|
|
6
|
+
return env.PARX_HOME;
|
|
7
|
+
if (env.PARALLEL_CODE_HOME)
|
|
8
|
+
return env.PARALLEL_CODE_HOME;
|
|
9
|
+
return join(baseDeConfiguracion(env, plataforma), 'parx');
|
|
10
|
+
}
|
|
11
|
+
function baseDeConfiguracion(env, plataforma) {
|
|
12
|
+
if (plataforma === 'win32')
|
|
13
|
+
return env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');
|
|
14
|
+
return env.XDG_CONFIG_HOME ?? join(homedir(), '.config');
|
|
15
|
+
}
|
|
16
|
+
export function migrarCarpetaVieja(env = process.env, plataforma = process.platform) {
|
|
17
|
+
if (env.PARX_HOME || env.PARALLEL_CODE_HOME)
|
|
18
|
+
return;
|
|
19
|
+
const vieja = join(baseDeConfiguracion(env, plataforma), 'parallel-code');
|
|
20
|
+
const nueva = carpetaDeConfiguracion(env, plataforma);
|
|
21
|
+
try {
|
|
22
|
+
if (existsSync(vieja) && !existsSync(nueva))
|
|
23
|
+
renameSync(vieja, nueva);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const VERSION = leerVersion();
|
|
29
|
+
function leerVersion() {
|
|
30
|
+
try {
|
|
31
|
+
const v = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
|
|
32
|
+
return typeof v === 'string' ? v : '0.0.0';
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return '0.0.0';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export const NOMBRE = 'parx';
|
|
39
|
+
export function urlDelServidor(env = process.env) {
|
|
40
|
+
return (env.PARALLEL_URL ?? 'https://getparallel.net').replace(/\/+$/, '');
|
|
41
|
+
}
|
|
42
|
+
export const FORMA_TOKEN = /^par_term_[A-Za-z0-9_-]{40}$/;
|
|
43
|
+
export const FORMA_CLAVE = /^par_key_[A-Za-z0-9_-]{40}$/;
|
|
44
|
+
export function esCredencial(x) {
|
|
45
|
+
return typeof x === 'string' && (FORMA_TOKEN.test(x) || FORMA_CLAVE.test(x));
|
|
46
|
+
}
|
|
47
|
+
export function credencialesDelEntorno(env = process.env) {
|
|
48
|
+
const clave = (env.PARALLEL_API_KEY ?? env.PARX_API_KEY ?? '').trim();
|
|
49
|
+
if (!clave)
|
|
50
|
+
return null;
|
|
51
|
+
if (!FORMA_CLAVE.test(clave))
|
|
52
|
+
return null;
|
|
53
|
+
return { token: clave, correo: '', servidor: urlDelServidor(env), creadas: '', tipo: 'entorno' };
|
|
54
|
+
}
|
|
55
|
+
const MODELO_VIEJO = { rapido: 'swift', potente: 'stride', maximo: 'summit', swift: 'swift', stride: 'stride', summit: 'summit' };
|
|
56
|
+
const MODO_VIEJO = { preguntar: 'ask', ask: 'ask', auto: 'auto', leer: 'read', read: 'read' };
|
|
57
|
+
function asegurarCarpeta(dir) {
|
|
58
|
+
if (!existsSync(dir))
|
|
59
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
60
|
+
}
|
|
61
|
+
export function leerCredenciales(dir = carpetaDeConfiguracion()) {
|
|
62
|
+
try {
|
|
63
|
+
const c = JSON.parse(readFileSync(join(dir, 'credenciales.json'), 'utf8'));
|
|
64
|
+
if (esCredencial(c.token) && typeof c.servidor === 'string') {
|
|
65
|
+
return { token: c.token, correo: String(c.correo ?? ''), servidor: c.servidor, creadas: String(c.creadas ?? ''), tipo: FORMA_CLAVE.test(c.token) ? 'clave' : 'navegador' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
export function guardarCredenciales(c, dir = carpetaDeConfiguracion()) {
|
|
73
|
+
asegurarCarpeta(dir);
|
|
74
|
+
const ruta = join(dir, 'credenciales.json');
|
|
75
|
+
writeFileSync(ruta, JSON.stringify(c, null, 2), { mode: 0o600 });
|
|
76
|
+
try {
|
|
77
|
+
chmodSync(ruta, 0o600);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function borrarCredenciales(dir = carpetaDeConfiguracion()) {
|
|
83
|
+
rmSync(join(dir, 'credenciales.json'), { force: true });
|
|
84
|
+
}
|
|
85
|
+
export function leerAjustes(dir = carpetaDeConfiguracion()) {
|
|
86
|
+
try {
|
|
87
|
+
const a = JSON.parse(readFileSync(join(dir, 'ajustes.json'), 'utf8'));
|
|
88
|
+
const out = {};
|
|
89
|
+
if (typeof a.modelo === 'string' && MODELO_VIEJO[a.modelo])
|
|
90
|
+
out.modelo = MODELO_VIEJO[a.modelo];
|
|
91
|
+
if (typeof a.modo === 'string' && MODO_VIEJO[a.modo])
|
|
92
|
+
out.modo = MODO_VIEJO[a.modo];
|
|
93
|
+
if (typeof a.avisos === 'boolean')
|
|
94
|
+
out.avisos = a.avisos;
|
|
95
|
+
const k = a.consejos;
|
|
96
|
+
if (k && typeof k === 'object') {
|
|
97
|
+
const n = (x) => (typeof x === 'number' && Number.isFinite(x) && x >= 0 ? Math.floor(x) : 0);
|
|
98
|
+
out.consejos = { sesiones: n(k.sesiones), siguiente: n(k.siguiente), ...(k.apagados === true ? { apagados: true } : {}) };
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function guardarAjustes(cambios, dir = carpetaDeConfiguracion()) {
|
|
107
|
+
asegurarCarpeta(dir);
|
|
108
|
+
const a = { ...leerAjustes(dir), ...cambios };
|
|
109
|
+
if (a.modo === 'bypass')
|
|
110
|
+
delete a.modo;
|
|
111
|
+
writeFileSync(join(dir, 'ajustes.json'), JSON.stringify(a, null, 2), { mode: 0o600 });
|
|
112
|
+
}
|
package/dist/consejos.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { capacidades } from "./marca.js";
|
|
2
|
+
import { t } from "./textos.js";
|
|
3
|
+
export const CONSEJOS = ['menu', 'modo', 'una', 'imagen', 'historial', 'pegar', 'deshacer', 'modelo', 'carriles', 'parar', 'cola', 'informe', 'mision', 'ensenar', 'saldo', 'cuenta', 'agents'];
|
|
4
|
+
export function consejo(id) {
|
|
5
|
+
const u = capacidades().unicode;
|
|
6
|
+
const tecla = {
|
|
7
|
+
menu: '/',
|
|
8
|
+
modo: u ? '⇧⇥' : 'Shift+Tab',
|
|
9
|
+
una: 'parx "task"',
|
|
10
|
+
historial: u ? '↑ ↓' : 'Up/Down',
|
|
11
|
+
pegar: 'Paste',
|
|
12
|
+
imagen: 'Drag image',
|
|
13
|
+
deshacer: '/undo',
|
|
14
|
+
modelo: '/model',
|
|
15
|
+
parar: 'Esc',
|
|
16
|
+
cola: 'Enter',
|
|
17
|
+
carriles: 'parx --lanes',
|
|
18
|
+
informe: '/report',
|
|
19
|
+
mision: '/mission',
|
|
20
|
+
ensenar: '/teach',
|
|
21
|
+
saldo: '/balance',
|
|
22
|
+
cuenta: 'parx account',
|
|
23
|
+
agents: 'AGENTS.md',
|
|
24
|
+
};
|
|
25
|
+
return { tecla: tecla[id], texto: t(`tip.${id}`) };
|
|
26
|
+
}
|
|
27
|
+
export function cuantosTocan(n) {
|
|
28
|
+
if (n <= 3)
|
|
29
|
+
return 3;
|
|
30
|
+
if (n <= 6)
|
|
31
|
+
return 2;
|
|
32
|
+
if (n <= 12)
|
|
33
|
+
return 1;
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
export function elegirConsejos(estado) {
|
|
37
|
+
const e = estado ? { ...estado } : { sesiones: 0, siguiente: 0 };
|
|
38
|
+
if (e.apagados)
|
|
39
|
+
return { ids: [], estado: e };
|
|
40
|
+
const sesion = e.sesiones + 1;
|
|
41
|
+
const n = cuantosTocan(sesion);
|
|
42
|
+
const desde = ((e.siguiente % CONSEJOS.length) + CONSEJOS.length) % CONSEJOS.length;
|
|
43
|
+
const ids = Array.from({ length: n }, (_, i) => CONSEJOS[(desde + i) % CONSEJOS.length]);
|
|
44
|
+
return { ids, estado: { ...e, sesiones: sesion, siguiente: (desde + n) % CONSEJOS.length } };
|
|
45
|
+
}
|
|
46
|
+
export function cambiarConsejos(estado, si) {
|
|
47
|
+
const e = estado ? { ...estado } : { sesiones: 0, siguiente: 0 };
|
|
48
|
+
if (si)
|
|
49
|
+
return { sesiones: 0, siguiente: e.siguiente };
|
|
50
|
+
return { ...e, apagados: true };
|
|
51
|
+
}
|
|
52
|
+
export function ordenDeConsejos(si) {
|
|
53
|
+
return `/tips ${si ? 'on' : 'off'}`;
|
|
54
|
+
}
|
package/dist/cuenta.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createInterface } from 'node:readline/promises';
|
|
3
|
+
import { borrarCredenciales, credencialesDelEntorno, FORMA_CLAVE, guardarCredenciales, leerCredenciales, urlDelServidor, } from "./config.js";
|
|
4
|
+
import { cerrarSesion, correoVisible, ErrorDeParallel, quienEs, leerCuenta, pedirCodigo, recogerToken } from "./api.js";
|
|
5
|
+
import { t } from "./textos.js";
|
|
6
|
+
import { c, capacidades, sinColor } from "./marca.js";
|
|
7
|
+
import { qrEnTexto } from "./qr.js";
|
|
8
|
+
export function abrirNavegador(url) {
|
|
9
|
+
if (process.env.PARALLEL_NO_BROWSER)
|
|
10
|
+
return;
|
|
11
|
+
try {
|
|
12
|
+
const [programa, args] = process.platform === 'darwin'
|
|
13
|
+
? ['open', [url]]
|
|
14
|
+
: process.platform === 'win32'
|
|
15
|
+
? ['rundll32', ['url.dll,FileProtocolHandler', url]]
|
|
16
|
+
: ['xdg-open', [url]];
|
|
17
|
+
const hijo = spawn(programa, args, { stdio: 'ignore', detached: true, windowsHide: true });
|
|
18
|
+
hijo.on('error', () => { });
|
|
19
|
+
hijo.unref();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function pareceSinNavegador(env = process.env, plataforma = process.platform) {
|
|
25
|
+
if (env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY)
|
|
26
|
+
return true;
|
|
27
|
+
if (plataforma === 'linux' && !env.DISPLAY && !env.WAYLAND_DISPLAY)
|
|
28
|
+
return true;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
export function formaDeLaOpcion(x) {
|
|
32
|
+
const v = x.trim().toLowerCase().replace(/^--/, '');
|
|
33
|
+
if (['1', 'navegador', 'browser', 'web', 'navigateur', 'browser-login'].includes(v))
|
|
34
|
+
return 'navegador';
|
|
35
|
+
if (['2', 'clave', 'api-key', 'apikey', 'key', 'with-api-key', 'chave', 'cle', 'clé', 'schlussel', 'schlüssel', 'chiave', 'sleutel'].includes(v))
|
|
36
|
+
return 'clave';
|
|
37
|
+
if (['3', 'sin-navegador', 'sin_navegador', 'no-browser', 'device', 'device-auth', 'dispositivo', 'ssh', 'sem-navegador', 'sans-navigateur', 'ohne-browser', 'senza-browser', 'zonder-browser'].includes(v))
|
|
38
|
+
return 'sin_navegador';
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const dormir = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
42
|
+
async function preguntarLinea(pregunta) {
|
|
43
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
44
|
+
try {
|
|
45
|
+
return await rl.question(pregunta);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
rl.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function elegirForma(escribir, tty) {
|
|
52
|
+
const ssh = pareceSinNavegador();
|
|
53
|
+
if (!tty)
|
|
54
|
+
return 'sin_navegador';
|
|
55
|
+
const filas = [
|
|
56
|
+
[t('entrar.navegador'), ssh ? '' : t('entrar.navegador_nota')],
|
|
57
|
+
[t('entrar.clave'), t('entrar.clave_nota')],
|
|
58
|
+
[t('entrar.sin_navegador'), ssh ? t('entrar.ssh_nota') : t('entrar.sin_navegador_nota')],
|
|
59
|
+
];
|
|
60
|
+
const ancho = Math.max(...filas.map(([a]) => a.length)) + 3;
|
|
61
|
+
escribir(` ${c.negrita(t('entrar.como'))}`);
|
|
62
|
+
escribir();
|
|
63
|
+
filas.forEach(([a, nota], i) => escribir(` ${c.claro(String(i + 1))} ${a.padEnd(ancho)}${c.gris(nota)}`));
|
|
64
|
+
escribir();
|
|
65
|
+
const porDefecto = ssh ? '3' : '1';
|
|
66
|
+
for (;;) {
|
|
67
|
+
const r = (await preguntarLinea(` ${c.claro('›')} ${c.gris(`(${porDefecto}) `)}`)).trim();
|
|
68
|
+
const forma = formaDeLaOpcion(r || porDefecto);
|
|
69
|
+
if (forma)
|
|
70
|
+
return forma;
|
|
71
|
+
escribir(c.gris(` ${t('entrar.elige')}`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function leerOculto(pregunta, entrada = process.stdin, salida = process.stdout) {
|
|
75
|
+
if (!entrada.isTTY) {
|
|
76
|
+
let todo = '';
|
|
77
|
+
for await (const trozo of entrada)
|
|
78
|
+
todo += String(trozo);
|
|
79
|
+
return todo.split(/\r?\n/).find((l) => l.trim())?.trim() ?? '';
|
|
80
|
+
}
|
|
81
|
+
salida.write(pregunta);
|
|
82
|
+
return new Promise((resolver, rechazar) => {
|
|
83
|
+
let valor = '';
|
|
84
|
+
const fin = (e) => {
|
|
85
|
+
entrada.removeListener('data', alLeer);
|
|
86
|
+
try {
|
|
87
|
+
entrada.setRawMode(false);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
}
|
|
91
|
+
entrada.pause();
|
|
92
|
+
salida.write('\n');
|
|
93
|
+
if (e)
|
|
94
|
+
rechazar(e);
|
|
95
|
+
else
|
|
96
|
+
resolver(valor.trim());
|
|
97
|
+
};
|
|
98
|
+
const alLeer = (trozo) => {
|
|
99
|
+
for (const ch of String(trozo)) {
|
|
100
|
+
if (ch === '\r' || ch === '\n')
|
|
101
|
+
return fin();
|
|
102
|
+
if (ch === '\u0003')
|
|
103
|
+
return fin(new ErrorDeParallel('interrumpido'));
|
|
104
|
+
if (ch === '\u007f' || ch === '\b')
|
|
105
|
+
valor = valor.slice(0, -1);
|
|
106
|
+
else if (ch >= ' ')
|
|
107
|
+
valor += ch;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
entrada.setRawMode(true);
|
|
111
|
+
entrada.resume();
|
|
112
|
+
entrada.on('data', alLeer);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async function esperarAprobacion(servidor, d) {
|
|
116
|
+
const fin = Date.now() + d.caduca_en * 1000;
|
|
117
|
+
let intervalo = Math.max(2, d.intervalo) * 1000;
|
|
118
|
+
while (Date.now() < fin) {
|
|
119
|
+
await dormir(intervalo);
|
|
120
|
+
let r;
|
|
121
|
+
try {
|
|
122
|
+
r = await recogerToken(servidor, d.codigo_dispositivo);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
if (e instanceof ErrorDeParallel && e.codigo === 'red') {
|
|
126
|
+
intervalo = Math.min(intervalo * 2, 15_000);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
if (r.estado === 'aprobado')
|
|
132
|
+
return { token: r.token, correo: r.correo };
|
|
133
|
+
if (r.estado === 'rechazado')
|
|
134
|
+
throw new Error(t('cuenta.rechazado'));
|
|
135
|
+
if (r.estado === 'caducado')
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
throw new Error(t('cuenta.caducado'));
|
|
139
|
+
}
|
|
140
|
+
async function conDispositivo(servidor, escribir, abrir) {
|
|
141
|
+
const d = await pedirCodigo(servidor);
|
|
142
|
+
escribir();
|
|
143
|
+
if (abrir) {
|
|
144
|
+
escribir(` ${t('cuenta.abre')}:`);
|
|
145
|
+
escribir(` ${c.marca(d.url)}`);
|
|
146
|
+
escribir();
|
|
147
|
+
escribir(` ${t('cuenta.codigo')}: ${c.negrita(c.claro(d.codigo_usuario))}`);
|
|
148
|
+
escribir();
|
|
149
|
+
escribir(c.gris(` ${t('cuenta.abriendo')}`));
|
|
150
|
+
abrirNavegador(d.url_completa);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
escribir(` ${t('entrar.abre_en_otro')}`);
|
|
154
|
+
escribir(` ${c.marca(d.url_corta ?? d.url)}`);
|
|
155
|
+
escribir();
|
|
156
|
+
escribir(` ${t('cuenta.codigo')}: ${c.negrita(c.claro(d.codigo_usuario))}`);
|
|
157
|
+
const caps = capacidades();
|
|
158
|
+
const qr = qrEnTexto(d.url_corta_completa ?? d.url_completa, { color: caps.color > 0, unicode: caps.unicode });
|
|
159
|
+
if (qr && caps.ancho >= sinColor(qr[0]).length + 4) {
|
|
160
|
+
escribir();
|
|
161
|
+
escribir(c.gris(` ${t('entrar.escanea')}`));
|
|
162
|
+
escribir();
|
|
163
|
+
for (const l of qr)
|
|
164
|
+
escribir(` ${l}`);
|
|
165
|
+
}
|
|
166
|
+
escribir();
|
|
167
|
+
}
|
|
168
|
+
escribir(c.gris(` ${t('cuenta.esperando')}`));
|
|
169
|
+
const r = await esperarAprobacion(servidor, d);
|
|
170
|
+
return { token: r.token, correo: r.correo, servidor, creadas: new Date().toISOString(), tipo: 'navegador' };
|
|
171
|
+
}
|
|
172
|
+
async function conClave(servidor, escribir, clavePasada) {
|
|
173
|
+
let clave = clavePasada;
|
|
174
|
+
if (!clave) {
|
|
175
|
+
if (process.stdin.isTTY) {
|
|
176
|
+
escribir(c.gris(` ${t('entrar.crea_clave')}`));
|
|
177
|
+
escribir();
|
|
178
|
+
}
|
|
179
|
+
clave = await leerOculto(` ${t('entrar.pega_clave')}: `);
|
|
180
|
+
}
|
|
181
|
+
if (!FORMA_CLAVE.test(clave))
|
|
182
|
+
throw new Error(t('entrar.clave_mala'));
|
|
183
|
+
let cuenta;
|
|
184
|
+
try {
|
|
185
|
+
cuenta = await leerCuenta(servidor, clave);
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
if (e instanceof ErrorDeParallel && e.codigo === 'sesion')
|
|
189
|
+
throw new Error(t('entrar.clave_no_vale'));
|
|
190
|
+
throw e;
|
|
191
|
+
}
|
|
192
|
+
return { cred: { token: clave, correo: cuenta.correo, servidor, creadas: new Date().toISOString(), tipo: 'clave' }, cuenta };
|
|
193
|
+
}
|
|
194
|
+
export async function entrar(escribir, opciones = {}) {
|
|
195
|
+
const servidor = urlDelServidor();
|
|
196
|
+
const tty = opciones.tty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
197
|
+
const antes = leerCredenciales();
|
|
198
|
+
if (antes && tty) {
|
|
199
|
+
escribir(c.gris(` ${t('entrar.ya', { correo: correoVisible(antes.correo) || '·' })}`));
|
|
200
|
+
escribir();
|
|
201
|
+
}
|
|
202
|
+
const forma = opciones.forma ?? (await elegirForma(escribir, tty));
|
|
203
|
+
let cred;
|
|
204
|
+
if (forma === 'clave') {
|
|
205
|
+
const r = await conClave(servidor, escribir);
|
|
206
|
+
cred = r.cred;
|
|
207
|
+
guardarCredenciales(cred);
|
|
208
|
+
escribir(`${c.ok('✓')} ${t('entrar.ok_clave', { correo: quienEs(r.cuenta) || '·', nombre: r.cuenta.nombre ?? '' })}`);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
cred = await conDispositivo(servidor, escribir, forma === 'navegador');
|
|
212
|
+
guardarCredenciales(cred);
|
|
213
|
+
escribir(`${c.ok('✓')} ${correoVisible(cred.correo) ? t('cuenta.ok', { correo: correoVisible(cred.correo) }) : t('entrar.ok_sin_correo')}`);
|
|
214
|
+
}
|
|
215
|
+
if (antes && antes.tipo === 'navegador' && antes.token !== cred.token) {
|
|
216
|
+
try {
|
|
217
|
+
await cerrarSesion(antes.servidor, antes.token);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return cred;
|
|
223
|
+
}
|
|
224
|
+
export function credencialesActivas() {
|
|
225
|
+
return credencialesDelEntorno() ?? leerCredenciales();
|
|
226
|
+
}
|
|
227
|
+
export async function salir(escribir, todas = false) {
|
|
228
|
+
const entorno = credencialesDelEntorno();
|
|
229
|
+
const cred = leerCredenciales();
|
|
230
|
+
const usar = cred ?? entorno;
|
|
231
|
+
if (todas && usar) {
|
|
232
|
+
const r = await cerrarSesion(usar.servidor, usar.token, true);
|
|
233
|
+
if (cred)
|
|
234
|
+
borrarCredenciales();
|
|
235
|
+
escribir(t('salir.todas', { n: r.cerradas ?? 0 }));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (cred) {
|
|
239
|
+
if (cred.tipo !== 'clave') {
|
|
240
|
+
try {
|
|
241
|
+
await cerrarSesion(cred.servidor, cred.token);
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
borrarCredenciales();
|
|
247
|
+
escribir(cred.tipo === 'clave' ? t('salir.clave') : t('cuenta.fuera'));
|
|
248
|
+
}
|
|
249
|
+
else if (!entorno) {
|
|
250
|
+
escribir(t('cuenta.fuera'));
|
|
251
|
+
}
|
|
252
|
+
if (entorno)
|
|
253
|
+
escribir(t('salir.entorno'));
|
|
254
|
+
}
|
|
255
|
+
export function comoHasEntrado(cred, cuenta, equipoLocal) {
|
|
256
|
+
if (cred.tipo === 'entorno')
|
|
257
|
+
return t('cuenta.via_entorno');
|
|
258
|
+
if (cuenta.via === 'clave' || cred.tipo === 'clave')
|
|
259
|
+
return t('cuenta.via_clave', { nombre: cuenta.nombre ?? '', prefijo: cuenta.prefijo ?? cred.token.slice(0, 12) });
|
|
260
|
+
return t('cuenta.via_navegador', { equipo: cuenta.nombre ?? equipoLocal });
|
|
261
|
+
}
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function diffDeLineas(antes, despues) {
|
|
2
|
+
const a = antes === '' ? [] : antes.split('\n');
|
|
3
|
+
const b = despues === '' ? [] : despues.split('\n');
|
|
4
|
+
let ini = 0;
|
|
5
|
+
while (ini < a.length && ini < b.length && a[ini] === b[ini])
|
|
6
|
+
ini++;
|
|
7
|
+
let finA = a.length;
|
|
8
|
+
let finB = b.length;
|
|
9
|
+
while (finA > ini && finB > ini && a[finA - 1] === b[finB - 1]) {
|
|
10
|
+
finA--;
|
|
11
|
+
finB--;
|
|
12
|
+
}
|
|
13
|
+
const ma = a.slice(ini, finA);
|
|
14
|
+
const mb = b.slice(ini, finB);
|
|
15
|
+
if (ma.length * mb.length > 4_000_000)
|
|
16
|
+
return null;
|
|
17
|
+
const n = ma.length;
|
|
18
|
+
const m = mb.length;
|
|
19
|
+
const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
20
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
21
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
22
|
+
dp[i][j] = ma[i] === mb[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const out = [];
|
|
26
|
+
for (let k = 0; k < ini; k++)
|
|
27
|
+
out.push({ tipo: ' ', texto: a[k], antes: k + 1, despues: k + 1 });
|
|
28
|
+
let i = 0;
|
|
29
|
+
let j = 0;
|
|
30
|
+
while (i < n || j < m) {
|
|
31
|
+
if (i < n && j < m && ma[i] === mb[j]) {
|
|
32
|
+
out.push({ tipo: ' ', texto: ma[i], antes: ini + i + 1, despues: ini + j + 1 });
|
|
33
|
+
i++;
|
|
34
|
+
j++;
|
|
35
|
+
}
|
|
36
|
+
else if (i < n && (j >= m || dp[i + 1][j] >= dp[i][j + 1])) {
|
|
37
|
+
out.push({ tipo: '-', texto: ma[i], antes: ini + i + 1, despues: null });
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
out.push({ tipo: '+', texto: mb[j], antes: null, despues: ini + j + 1 });
|
|
42
|
+
j++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (let k = 0; k < a.length - finA; k++)
|
|
46
|
+
out.push({ tipo: ' ', texto: a[finA + k], antes: finA + k + 1, despues: finB + k + 1 });
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
export function trozos(lineas, contexto = 2) {
|
|
50
|
+
const mostrar = new Array(lineas.length).fill(false);
|
|
51
|
+
lineas.forEach((l, i) => {
|
|
52
|
+
if (l.tipo !== ' ')
|
|
53
|
+
for (let k = Math.max(0, i - contexto); k <= Math.min(lineas.length - 1, i + contexto); k++)
|
|
54
|
+
mostrar[k] = true;
|
|
55
|
+
});
|
|
56
|
+
const out = [];
|
|
57
|
+
let saltado = false;
|
|
58
|
+
lineas.forEach((l, i) => {
|
|
59
|
+
if (mostrar[i]) {
|
|
60
|
+
if (saltado && out.length)
|
|
61
|
+
out.push(null);
|
|
62
|
+
saltado = false;
|
|
63
|
+
out.push(l);
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
saltado = true;
|
|
67
|
+
});
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
export function contarCambios(lineas) {
|
|
71
|
+
let mas = 0;
|
|
72
|
+
let menos = 0;
|
|
73
|
+
for (const l of lineas) {
|
|
74
|
+
if (l.tipo === '+')
|
|
75
|
+
mas++;
|
|
76
|
+
else if (l.tipo === '-')
|
|
77
|
+
menos++;
|
|
78
|
+
}
|
|
79
|
+
return { mas, menos };
|
|
80
|
+
}
|