@getparallel/parx 0.5.0 → 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/ui.js CHANGED
@@ -47,6 +47,7 @@ export class Pantalla {
47
47
  esperaActiva = null;
48
48
  trabajoEnLinea = null;
49
49
  carril;
50
+ espejo = null;
50
51
  tty;
51
52
  constructor(rl, tty = Boolean(process.stdout.isTTY && process.stdin.isTTY), carril = null) {
52
53
  this.rl = rl;
@@ -110,6 +111,8 @@ export class Pantalla {
110
111
  this.pintarEnLinea();
111
112
  }
112
113
  escribir(linea = '', grupo = 'nota') {
114
+ if (linea.trim())
115
+ this.espejo?.evento('nota', { texto: sinColor(linea).trim() });
113
116
  if (!linea.trim()) {
114
117
  if (!this.terminaEnBlanco() && !this.carril)
115
118
  this.bloque(() => [''], this.ultimoGrupo ?? 'nota');
@@ -119,6 +122,7 @@ export class Pantalla {
119
122
  }
120
123
  lecturas = null;
121
124
  accion(verbo, objeto, nota = '') {
125
+ this.espejo?.evento('accion', { verbo: sinColor(verbo), objeto: sinColor(objeto), nota: sinColor(nota) });
122
126
  if (this.superficie && verbo === t('herr.leer')) {
123
127
  const l = this.lecturas;
124
128
  if (l && this.superficie.ultimoBloque() === l.bloque) {
@@ -146,6 +150,7 @@ export class Pantalla {
146
150
  .filter((l) => l.trim() !== '' && !RELLENO_DE_TESTS.test(l));
147
151
  if (!lineas.length)
148
152
  return;
153
+ this.espejo?.evento('salida', { lineas: lineas.slice(-6).map((l) => sinColor(l)) });
149
154
  const ultimas = lineas.slice(-maxLineas);
150
155
  const sobran = lineas.length - ultimas.length;
151
156
  this.bloque((ancho) => {
@@ -160,15 +165,19 @@ export class Pantalla {
160
165
  }, 'accion');
161
166
  }
162
167
  detalle(texto, error = false) {
168
+ this.espejo?.evento('detalle', { texto: sinColor(texto), error });
163
169
  this.bloque((ancho) => [`${' '.repeat(MARGEN + sangriaDeDetalle())}${recortar(error ? c.error(`${SIMBOLOS.falla} ${texto}`) : c.gris(texto), anchoUtil(ancho) - sangriaDeDetalle())}`], 'accion');
164
170
  }
165
171
  aviso(texto) {
172
+ this.espejo?.evento('aviso', { texto: sinColor(texto) });
166
173
  this.bloque(bloqueDeTexto(`${c.aviso('!')} ${texto}`, ' '), 'aviso');
167
174
  }
168
175
  error(texto) {
176
+ this.espejo?.evento('error', { texto: sinColor(texto) });
169
177
  this.bloque(bloqueDeTexto(`${c.error(SIMBOLOS.falla)} ${texto}`, ' '), 'error');
170
178
  }
171
179
  cierre(texto) {
180
+ this.espejo?.evento('cierre', { texto: sinColor(texto) });
172
181
  this.bloque(bloqueDeCierre(texto, c.ok(SIMBOLOS.hecho)), 'cierre');
173
182
  }
174
183
  trabajoPropio() {
@@ -205,6 +214,7 @@ export class Pantalla {
205
214
  process.stdout.write(`\r\x1b[2K${' '.repeat(MARGEN)}${recortar(filaDeTrabajo(this.trabajoEnLinea, Date.now()), ancho - MARGEN - 2)}`);
206
215
  }
207
216
  progreso(acciones) {
217
+ this.espejo?.progreso(acciones);
208
218
  if (this.carril) {
209
219
  this.carril.trabajo.acciones = acciones;
210
220
  this.superficie?.actualizarCarriles();
@@ -236,6 +246,10 @@ export class Pantalla {
236
246
  }
237
247
  diff(lineas, max = 40) {
238
248
  const visibles = trozos(lineas);
249
+ this.espejo?.evento('diff', {
250
+ lineas: visibles.slice(0, 40).map((l) => (l === null ? { tipo: '⋮', texto: '' } : { tipo: l.tipo, texto: l.texto, num: l.despues ?? l.antes ?? null })),
251
+ mas: Math.max(0, visibles.length - 40),
252
+ });
239
253
  this.bloque((ancho) => {
240
254
  const s = sangriaDeDetalle();
241
255
  const util = anchoUtil(ancho) - s;
@@ -270,7 +284,21 @@ export class Pantalla {
270
284
  this.aviso(o.aviso);
271
285
  const texto = this.carril ? `${t('carriles.carril', { n: this.carril.numero })}: ${pregunta}` : pregunta;
272
286
  if (this.superficie) {
273
- const r = await this.superficie.preguntar(texto, tipo);
287
+ const op = opcionesDePregunta(tipo);
288
+ const remota = this.espejo?.preguntaRemota(sinColor(texto), tipo, op.opciones.map((etiqueta, i) => ({ etiqueta, valor: op.valores[i] }))) ?? null;
289
+ let desdeFuera = false;
290
+ const externo = remota?.deFuera?.then((r) => {
291
+ desdeFuera = true;
292
+ return r;
293
+ });
294
+ const r = await this.superficie.preguntar(texto, tipo, externo);
295
+ remota?.contestada(r, desdeFuera ? 'movil' : 'ordenador');
296
+ if (desdeFuera) {
297
+ const o2 = opcionesDePregunta(tipo);
298
+ const etiqueta = o2.opciones[o2.valores.indexOf(r)] ?? t('tui.no');
299
+ this.bloque(bloqueDeTexto(`${c.gris('?')} ${texto} ${c.gris('→')} ${r === 'no' ? c.gris(etiqueta) : c.marca(etiqueta)} ${c.gris(`· ${t('remoto.desde_el_movil')}`)}`, ' '), 'aviso');
300
+ return r;
301
+ }
274
302
  if (r !== 'si' && tipo !== 'bypass') {
275
303
  const o2 = opcionesDePregunta(tipo);
276
304
  const etiqueta = o2.opciones[o2.valores.indexOf(r)] ?? t('tui.no');
@@ -293,6 +321,10 @@ export class Pantalla {
293
321
  }
294
322
  }
295
323
  texto(md) {
324
+ this.espejo?.evento('texto', { md });
325
+ this.pintarTexto(md);
326
+ }
327
+ pintarTexto(md) {
296
328
  this.terminarRevelado();
297
329
  this.bloque(bloqueDeMarkdown(md), 'texto');
298
330
  }
@@ -304,10 +336,11 @@ export class Pantalla {
304
336
  f?.();
305
337
  }
306
338
  textoAnimado(md, o = {}) {
339
+ this.espejo?.evento('texto', { md });
307
340
  this.terminarRevelado();
308
341
  const conSuperficie = this.superficie !== null;
309
342
  if (!this.animar || this.carril || !this.tty || (!conSuperficie && !process.stdout.isTTY)) {
310
- this.texto(md);
343
+ this.pintarTexto(md);
311
344
  return Promise.resolve();
312
345
  }
313
346
  const completo = bloqueDeMarkdown(md);
@@ -0,0 +1,341 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { execFile } from 'node:child_process';
10
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { pathToFileURL } from 'node:url';
13
+ import { carpetaDeConfiguracion, leerAjustes, VERSION } from "./config.js";
14
+ import { comprobacionApagada, esMasNueva, ordenDeActualizar, PAQUETE, PAQUETE_EN_URL, partes, rutaDelCli } from "./actualizar.js";
15
+ import { clavesDeConfianza, firmaValida, integridadCoincide } from "./firmas.js";
16
+ import { escribirFicheros, leerTgz } from "./tar.js";
17
+ export const CARPETA = 'versiones';
18
+ const ESTADO = 'estado.json';
19
+ const MAX_TGZ = 20 * 1024 * 1024;
20
+ const PLAZO_DESCARGA_MS = 60_000;
21
+ const PLAZO_ARRANQUE_MS = 20_000;
22
+ const GUARDAR = 2;
23
+ export function carpetaDeVersiones(dir = carpetaDeConfiguracion()) {
24
+ return join(dir, CARPETA);
25
+ }
26
+ export function leerEstado(dir = carpetaDeConfiguracion()) {
27
+ try {
28
+ const e = JSON.parse(readFileSync(join(carpetaDeVersiones(dir), ESTADO), 'utf8'));
29
+ const candidatas = {};
30
+ for (const [v, c] of Object.entries(e.candidatas ?? {})) {
31
+ if (!partes(v) || !c || typeof c !== 'object')
32
+ continue;
33
+ const x = c;
34
+ candidatas[v] = {
35
+ descargada: typeof x.descargada === 'string' ? x.descargada : '',
36
+ probada: x.probada === true,
37
+ intentos: typeof x.intentos === 'number' && x.intentos >= 0 ? Math.floor(x.intentos) : 0,
38
+ arranca: x.arranca === true,
39
+ anunciada: x.anunciada === true,
40
+ ...(typeof x.mala === 'string' ? { mala: x.mala } : {}),
41
+ ...(typeof x.novedad === 'string' ? { novedad: x.novedad } : {}),
42
+ };
43
+ }
44
+ return { candidatas };
45
+ }
46
+ catch {
47
+ return { candidatas: {} };
48
+ }
49
+ }
50
+ export function guardarEstado(e, dir = carpetaDeConfiguracion()) {
51
+ try {
52
+ const carpeta = carpetaDeVersiones(dir);
53
+ if (!existsSync(carpeta))
54
+ mkdirSync(carpeta, { recursive: true, mode: 0o700 });
55
+ writeFileSync(join(carpeta, ESTADO), JSON.stringify(e, null, 2), { mode: 0o600 });
56
+ }
57
+ catch {
58
+ }
59
+ }
60
+ export function autoactualizarApagada(env = process.env, ajustes = leerAjustes()) {
61
+ const si = (x) => Boolean(x && x !== '0' && x.toLowerCase() !== 'false');
62
+ return comprobacionApagada(env) || si(env.PARX_NO_AUTO_UPDATE) || ajustes.autoactualizar === false;
63
+ }
64
+ export function gestorPermiteAutoactualizar(gestor) {
65
+ return gestor !== 'npx' && gestor !== 'desarrollo';
66
+ }
67
+ export function rutaDeLaVersion(version, dir = carpetaDeConfiguracion()) {
68
+ const carpeta = join(carpetaDeVersiones(dir), version);
69
+ return { carpeta, cli: join(carpeta, 'dist', 'cli.js') };
70
+ }
71
+ export function novedadDelChangelog(texto, version) {
72
+ const re = new RegExp(`^## ${version.replace(/\./g, '\\.')}\\b[^\\n]*\\n([\\s\\S]*?)(?=^## |(?![\\s\\S]))`, 'm');
73
+ const m = re.exec(texto);
74
+ if (!m)
75
+ return null;
76
+ const linea = m[1].split('\n').map((l) => l.trim()).find((l) => /^[-*]\s+\S/.test(l));
77
+ if (!linea)
78
+ return null;
79
+ const limpia = linea
80
+ .replace(/^[-*]\s+/, '')
81
+ .replace(/\*\*([^*]+)\*\*/g, '$1')
82
+ .replace(/`([^`]+)`/g, '$1')
83
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
84
+ .replace(/\s+/g, ' ')
85
+ .trim();
86
+ if (!limpia)
87
+ return null;
88
+ const frase = (/^(.{20,}?\.)\s/.exec(limpia)?.[1] ?? limpia).replace(/[;:,]$/, '');
89
+ if (frase.length <= 140)
90
+ return frase;
91
+ const corte = frase.slice(0, 139);
92
+ const espacio = corte.lastIndexOf(' ');
93
+ return `${(espacio > 80 ? corte.slice(0, espacio) : corte).replace(/[\s;:,.]+$/, '')}…`;
94
+ }
95
+ function registroDe(opts) {
96
+ return (opts.registro ?? opts.env?.PARX_REGISTRY ?? process.env.PARX_REGISTRY ?? 'https://registry.npmjs.org').replace(/\/+$/, '');
97
+ }
98
+ export async function manifiestoDe(version, opts = {}) {
99
+ const registro = registroDe(opts);
100
+ try {
101
+ const r = await (opts.fetch ?? fetch)(`${registro}/${PAQUETE_EN_URL}/${version}`, {
102
+ headers: { accept: 'application/json', 'user-agent': `parx/${VERSION}` },
103
+ signal: AbortSignal.timeout(opts.plazoMs ?? 10_000),
104
+ });
105
+ if (!r.ok)
106
+ return null;
107
+ const j = (await r.json());
108
+ if (j.version !== version || !j.dist || typeof j.dist.integrity !== 'string' || typeof j.dist.tarball !== 'string')
109
+ return null;
110
+ return { version, integrity: j.dist.integrity, tarball: j.dist.tarball, firmas: j.dist.signatures };
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ export function tarballDelRegistro(tarball, registro) {
117
+ try {
118
+ const t = new URL(tarball);
119
+ const r = new URL(registro);
120
+ if (t.host !== r.host)
121
+ return false;
122
+ if (t.protocol !== r.protocol)
123
+ return false;
124
+ return t.protocol === 'https:' || t.hostname === '127.0.0.1' || t.hostname === 'localhost';
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ }
130
+ async function bajar(url, opts) {
131
+ try {
132
+ const r = await (opts.fetch ?? fetch)(url, { headers: { 'user-agent': `parx/${VERSION}` }, signal: AbortSignal.timeout(opts.plazoMs ?? PLAZO_DESCARGA_MS) });
133
+ if (!r.ok)
134
+ return null;
135
+ const largo = Number(r.headers.get('content-length') ?? 0);
136
+ if (largo > MAX_TGZ)
137
+ return null;
138
+ const b = Buffer.from(await r.arrayBuffer());
139
+ return b.length > MAX_TGZ ? null : b;
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ export function probarQueArranca(cli, version, opts = {}) {
146
+ const env = { ...(opts.env ?? process.env), PARX_LANZADO_DESDE: 'prueba', PARX_NO_UPDATE_CHECK: '1' };
147
+ delete env.PARX_PRUEBAS;
148
+ return new Promise((resolver) => {
149
+ execFile(opts.node ?? process.execPath, [cli, '--version'], { env, timeout: opts.plazoMs ?? PLAZO_ARRANQUE_MS, windowsHide: true }, (error, stdout) => {
150
+ resolver(!error && String(stdout).trim() === `parx ${version}`);
151
+ });
152
+ });
153
+ }
154
+ export async function prepararVersion(version, opts = {}) {
155
+ const dir = opts.dir ?? carpetaDeConfiguracion();
156
+ if (!partes(version))
157
+ return { ok: false, motivo: 'manifiesto' };
158
+ const estado = leerEstado(dir);
159
+ const ya = estado.candidatas[version];
160
+ const { carpeta, cli } = rutaDeLaVersion(version, dir);
161
+ if (ya?.probada && !ya.mala && existsSync(cli))
162
+ return { ok: true, version, novedad: ya.novedad ?? null, yaEstaba: true };
163
+ const registro = registroDe(opts);
164
+ const m = await manifiestoDe(version, opts);
165
+ if (!m)
166
+ return { ok: false, motivo: 'manifiesto' };
167
+ if (!tarballDelRegistro(m.tarball, registro))
168
+ return { ok: false, motivo: 'manifiesto' };
169
+ const claves = opts.claves ?? clavesDeConfianza(opts.env ?? process.env);
170
+ if (!firmaValida({ nombre: PAQUETE, version, integrity: m.integrity, firmas: m.firmas }, claves))
171
+ return { ok: false, motivo: 'firma' };
172
+ const tgz = await bajar(m.tarball, opts);
173
+ if (!tgz)
174
+ return { ok: false, motivo: 'descarga' };
175
+ if (!integridadCoincide(tgz, m.integrity))
176
+ return { ok: false, motivo: 'integridad' };
177
+ let ficheros;
178
+ try {
179
+ ficheros = leerTgz(tgz);
180
+ }
181
+ catch {
182
+ return { ok: false, motivo: 'contenido' };
183
+ }
184
+ const pkg = ficheros.find((f) => f.ruta === 'package.json');
185
+ let manifiesto = {};
186
+ try {
187
+ manifiesto = pkg ? JSON.parse(pkg.contenido.toString('utf8')) : {};
188
+ }
189
+ catch {
190
+ }
191
+ if (manifiesto.name !== PAQUETE || manifiesto.version !== version || !ficheros.some((f) => f.ruta === 'dist/cli.js'))
192
+ return { ok: false, motivo: 'contenido' };
193
+ const temporal = `${carpeta}.tmp`;
194
+ try {
195
+ rmSync(temporal, { recursive: true, force: true });
196
+ rmSync(carpeta, { recursive: true, force: true });
197
+ mkdirSync(temporal, { recursive: true, mode: 0o700 });
198
+ escribirFicheros(ficheros, temporal);
199
+ }
200
+ catch {
201
+ rmSync(temporal, { recursive: true, force: true });
202
+ return { ok: false, motivo: 'disco' };
203
+ }
204
+ const arranca = await probarQueArranca(join(temporal, 'dist', 'cli.js'), version, { node: opts.node, env: opts.env });
205
+ if (!arranca) {
206
+ rmSync(temporal, { recursive: true, force: true });
207
+ const e = leerEstado(dir);
208
+ e.candidatas[version] = { descargada: new Date(opts.ahora ?? Date.now()).toISOString(), probada: false, intentos: 0, arranca: false, anunciada: false, mala: 'no_arranca' };
209
+ guardarEstado(e, dir);
210
+ return { ok: false, motivo: 'no_arranca' };
211
+ }
212
+ try {
213
+ renameSync(temporal, carpeta);
214
+ }
215
+ catch {
216
+ rmSync(temporal, { recursive: true, force: true });
217
+ return { ok: false, motivo: 'disco' };
218
+ }
219
+ const changelog = ficheros.find((f) => f.ruta === 'CHANGELOG.md');
220
+ const novedad = changelog ? novedadDelChangelog(changelog.contenido.toString('utf8'), version) : null;
221
+ const e = leerEstado(dir);
222
+ e.candidatas[version] = { descargada: new Date(opts.ahora ?? Date.now()).toISOString(), probada: true, intentos: 0, arranca: false, anunciada: false, ...(novedad ? { novedad } : {}) };
223
+ guardarEstado(e, dir);
224
+ limpiarVersiones(dir);
225
+ return { ok: true, version, novedad, yaEstaba: false };
226
+ }
227
+ export function limpiarVersiones(dir = carpetaDeConfiguracion()) {
228
+ const e = leerEstado(dir);
229
+ const carpeta = carpetaDeVersiones(dir);
230
+ const buenas = Object.entries(e.candidatas)
231
+ .filter(([, c]) => c.probada && !c.mala)
232
+ .map(([v]) => v)
233
+ .sort((a, b) => (esMasNueva(a, b) ? -1 : esMasNueva(b, a) ? 1 : 0));
234
+ const quedarse = new Set(buenas.slice(0, GUARDAR));
235
+ let entradas = [];
236
+ try {
237
+ entradas = readdirSync(carpeta);
238
+ }
239
+ catch {
240
+ return;
241
+ }
242
+ for (const nombre of entradas) {
243
+ if (nombre === ESTADO)
244
+ continue;
245
+ if (!quedarse.has(nombre)) {
246
+ rmSync(join(carpeta, nombre), { recursive: true, force: true });
247
+ if (e.candidatas[nombre] && !e.candidatas[nombre].mala)
248
+ delete e.candidatas[nombre];
249
+ }
250
+ }
251
+ guardarEstado(e, dir);
252
+ }
253
+ export function marcarMala(version, motivo, dir = carpetaDeConfiguracion()) {
254
+ const e = leerEstado(dir);
255
+ const c = e.candidatas[version];
256
+ if (c)
257
+ c.mala = motivo;
258
+ else
259
+ e.candidatas[version] = { descargada: '', probada: false, intentos: 0, arranca: false, anunciada: false, mala: motivo };
260
+ guardarEstado(e, dir);
261
+ rmSync(rutaDeLaVersion(version, dir).carpeta, { recursive: true, force: true });
262
+ }
263
+ export function elegirVersionParaArrancar(opts = {}) {
264
+ const dir = opts.dir ?? carpetaDeConfiguracion();
265
+ const actual = opts.actual ?? VERSION;
266
+ const e = leerEstado(dir);
267
+ let retirada = null;
268
+ let cambios = false;
269
+ for (const [v, c] of Object.entries(e.candidatas)) {
270
+ if (!esMasNueva(v, actual)) {
271
+ rmSync(rutaDeLaVersion(v, dir).carpeta, { recursive: true, force: true });
272
+ delete e.candidatas[v];
273
+ cambios = true;
274
+ continue;
275
+ }
276
+ if (c.probada && !c.mala && !c.arranca && c.intentos >= 2) {
277
+ c.mala = 'no_confirmo';
278
+ rmSync(rutaDeLaVersion(v, dir).carpeta, { recursive: true, force: true });
279
+ retirada = v;
280
+ cambios = true;
281
+ }
282
+ }
283
+ const validas = Object.entries(e.candidatas)
284
+ .filter(([v, c]) => c.probada && !c.mala && existsSync(rutaDeLaVersion(v, dir).cli))
285
+ .map(([v]) => v)
286
+ .sort((a, b) => (esMasNueva(a, b) ? -1 : 1));
287
+ const version = validas[0];
288
+ if (version) {
289
+ e.candidatas[version].intentos++;
290
+ cambios = true;
291
+ }
292
+ if (cambios)
293
+ guardarEstado(e, dir);
294
+ return { elegida: version ? { version, cli: rutaDeLaVersion(version, dir).cli } : null, retirada };
295
+ }
296
+ export function confirmarArranque(opts = {}) {
297
+ const env = opts.env ?? process.env;
298
+ const desde = env.PARX_LANZADO_DESDE ?? '';
299
+ if (!partes(desde))
300
+ return null;
301
+ const dir = opts.dir ?? carpetaDeConfiguracion();
302
+ const actual = opts.actual ?? VERSION;
303
+ const e = leerEstado(dir);
304
+ const c = e.candidatas[actual];
305
+ if (!c)
306
+ return null;
307
+ const primera = !c.anunciada;
308
+ c.arranca = true;
309
+ c.intentos = 0;
310
+ c.anunciada = true;
311
+ guardarEstado(e, dir);
312
+ return primera ? { version: actual, novedad: c.novedad ?? null } : null;
313
+ }
314
+ export async function lanzarLaMasNueva(opts = {}) {
315
+ const env = opts.env ?? process.env;
316
+ if (env.PARX_LANZADO_DESDE)
317
+ return { lanzada: false, retirada: null };
318
+ if (autoactualizarApagada(env, opts.ajustes ?? leerAjustes(opts.dir)))
319
+ return { lanzada: false, retirada: null };
320
+ const gestor = opts.gestor ?? ordenDeActualizar(rutaDelCli()).gestor;
321
+ if (!gestorPermiteAutoactualizar(gestor))
322
+ return { lanzada: false, retirada: null };
323
+ let retirada = null;
324
+ for (let intento = 0; intento < 3; intento++) {
325
+ const eleccion = elegirVersionParaArrancar({ dir: opts.dir, actual: opts.actual });
326
+ retirada = eleccion.retirada ?? retirada;
327
+ if (!eleccion.elegida)
328
+ return { lanzada: false, retirada };
329
+ env.PARX_LANZADO_DESDE = opts.actual ?? VERSION;
330
+ try {
331
+ await (opts.importar ?? ((u) => import(__rewriteRelativeImportExtension(u))))(pathToFileURL(eleccion.elegida.cli).href);
332
+ return { lanzada: true, version: eleccion.elegida.version };
333
+ }
334
+ catch (e) {
335
+ delete env.PARX_LANZADO_DESDE;
336
+ marcarMala(eleccion.elegida.version, `import: ${e instanceof Error ? e.message.slice(0, 200) : String(e)}`, opts.dir);
337
+ retirada = eleccion.elegida.version;
338
+ }
339
+ }
340
+ return { lanzada: false, retirada };
341
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getparallel/parx",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "parx: the coding agent of Parallel in your terminal. Reads and edits your project, runs commands and tests, and does coding tasks end to end. Mac, Windows and Linux.",
5
5
  "keywords": [
6
6
  "parx",