@glassnote/client 2.4.4 → 2.4.6

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/INSTALACION.md CHANGED
@@ -86,7 +86,8 @@ se rompe en silencio es la que descarga la gente.
86
86
 
87
87
  ## Actualizaciones
88
88
 
89
- Instalado por npm, el cliente comprueba el registry cada 3 horas y se actualiza solo:
89
+ Instalado por npm, el cliente comprueba el registry a los 5 segundos de arrancar y cada
90
+ hora a partir de ahí, y se actualiza solo:
90
91
 
91
92
  - lee `desiredVersion` de los datos de usuario (`lastest` = seguir la última, o una
92
93
  versión exacta y clavada); son las mismas claves que usaba el updater de S3, para no
@@ -95,6 +96,18 @@ Instalado por npm, el cliente comprueba el registry cada 3 horas y se actualiza
95
96
  abrir. El npm **no** se corre con la app en marcha: en Windows el `electron.exe` en uso
96
97
  está bloqueado y npm no podría reemplazarlo.
97
98
 
99
+ El ayudante corre con un **node de fuera del paquete**, no con el electron de la propia
100
+ app: ese electron vive dentro del árbol que npm va a reemplazar y en Windows lo mantenía
101
+ bloqueado, así que npm fallaba y la actualización no se aplicaba nunca. Se busca, en
102
+ orden: `GLASSNOTE_NODE`, el node anotado en `~/.glassnote/node-path` al instalar, el que
103
+ bajó el instalador a `~/.glassnote/node-v*`, y el del PATH. A npm se lo llama por su
104
+ `npm-cli.js`, no por el shim: en Windows el instalador mete su Node al PATH de esa sola
105
+ sesión de PowerShell, y la app —que la abre el autoarranque— no lo tendría.
106
+
107
+ `glassnote status` dice cuál se va a usar. Si sale `node de update: NO ENCONTRADO`, la
108
+ auto-actualización no puede funcionar en esa máquina: hay que reinstalar el cliente
109
+ (`irm https://glassnote.intermark.ec/install.ps1 | iex`) o instalar Node.
110
+
98
111
  Por `npx` no se actualiza nada: la caché es efímera y npx ya baja la última versión cada
99
112
  vez. Tampoco se registra el arranque automático — apuntaría a una carpeta que se limpia
100
113
  sola.
package/autoupdate.js CHANGED
@@ -30,10 +30,12 @@ class AutoUpdate {
30
30
  this.checkForUpdates();
31
31
  }, 5000);
32
32
 
33
- // Verificación periódica cada hora (3600000 ms = 1 hora)
33
+ // Verificación periódica cada hora. Antes decía "cada hora" en el comentario pero
34
+ // la cuenta era 60 * 60 * 3000, o sea 3 horas: un cliente podía tardar toda una
35
+ // tarde en enterarse de una versión nueva.
34
36
  this.updateInterval = setInterval(() => {
35
37
  this.checkForUpdates();
36
- }, 60 * 60 * 3000); // 3 hora en milisegundos
38
+ }, 60 * 60 * 1000);
37
39
  }
38
40
 
39
41
  /**
package/bin/glassnote.js CHANGED
@@ -39,6 +39,22 @@ function npmEnv() {
39
39
  });
40
40
  }
41
41
 
42
+ // npm se invoca por su `npm-cli.js` con el node que lo trae, NO por el shim del PATH.
43
+ // En Windows el instalador puede bajarse un Node a ~/.glassnote y meterlo solo al PATH de
44
+ // la sesión de PowerShell que corrió la instalación: la app la arranca el autoarranque,
45
+ // con otro entorno, y ahí `npm` no existe. La auto-actualización moría en "npm no se
46
+ // reconoce como un comando" y nadie lo veía. Si no hay forma de resolver el npm-cli.js se
47
+ // vuelve al shim de siempre.
48
+ function runNpm(args, opts) {
49
+ const nodeBinary = mode.resolveNodeBinary(process.execPath);
50
+ const npmCli = mode.resolveNpmCli(nodeBinary);
51
+ const base = { stdio: 'inherit', shell: false, env: npmEnv() };
52
+ if (nodeBinary && npmCli) {
53
+ return spawnSync(nodeBinary, [npmCli].concat(args), Object.assign(base, opts || {}));
54
+ }
55
+ return spawnSync(NPM_CMD, args, Object.assign(base, { shell: IS_WIN }, opts || {}));
56
+ }
57
+
42
58
  // userData tira de `uuid`, y este CLI corre también donde el árbol de dependencias no
43
59
  // está (un tarball recién desempacado, un install a medias). Por eso se carga tarde y
44
60
  // tolerando el fallo: si no se puede dejar constancia, el peor caso es que la app
@@ -188,10 +204,10 @@ function addToPathUnix() {
188
204
  return { touched, line };
189
205
  }
190
206
 
191
- function addToPathWindows() {
207
+ function addToPathWindows(dir = mode.BIN_DIR) {
192
208
  // Nada de `setx`: trunca el PATH a 1024 caracteres y se come el de la gente.
193
209
  const ps = [
194
- "$dir = '" + mode.BIN_DIR.replace(/'/g, "''") + "'",
210
+ "$dir = '" + dir.replace(/'/g, "''") + "'",
195
211
  "$cur = [Environment]::GetEnvironmentVariable('Path','User')",
196
212
  "if ($null -eq $cur) { $cur = '' }",
197
213
  "if (($cur -split ';') -notcontains $dir) {",
@@ -204,7 +220,22 @@ function addToPathWindows() {
204
220
  encoding: 'utf8',
205
221
  });
206
222
  const added = res.status === 0 && String(res.stdout).includes('added');
207
- return { touched: added ? ['PATH de usuario (sin administrador)'] : [], line: mode.BIN_DIR };
223
+ return { touched: added ? ['PATH de usuario (sin administrador)'] : [], line: dir };
224
+ }
225
+
226
+ // El shim `glassnote.cmd` que crea npm invoca `node` del PATH. Cuando el Node lo bajó el
227
+ // instalador a ~/.glassnote, ese directorio solo quedaba en el PATH de la sesión de
228
+ // PowerShell que corrió la instalación: en una terminal nueva —o en cualquier proceso
229
+ // posterior— `glassnote` moría con «"node" no se reconoce como un comando». Se agrega el
230
+ // directorio del node al PATH de usuario, igual que BIN_DIR.
231
+ function addNodeToPathWindows() {
232
+ if (!IS_WIN) return null;
233
+ const nodeBinary = mode.resolveNodeBinary(process.execPath);
234
+ // Solo el node que trajimos nosotros: el del sistema ya está donde tiene que estar.
235
+ if (!nodeBinary || !mode.isInside(nodeBinary, mode.HOME_DIR)) return null;
236
+ const dir = path.dirname(nodeBinary);
237
+ const res = addToPathWindows(dir);
238
+ return res.touched.length ? dir : null;
208
239
  }
209
240
 
210
241
  function removeFromPathUnix() {
@@ -237,7 +268,10 @@ function removeFromPathWindows() {
237
268
  "$dir = '" + mode.BIN_DIR.replace(/'/g, "''") + "'",
238
269
  "$cur = [Environment]::GetEnvironmentVariable('Path','User')",
239
270
  "if ($null -eq $cur) { Write-Output 'absent'; exit }",
240
- "$new = (($cur -split ';') | Where-Object { $_ -ne '' -and $_ -ne $dir }) -join ';'",
271
+ // Se quita cualquier entrada bajo ~/.glassnote, no solo BIN_DIR: el install tambien
272
+ // agrega el directorio del node que bajo, y dejarlo seria una ruta muerta.
273
+ "$home2 = '" + mode.HOME_DIR.replace(/'/g, "''") + "'",
274
+ "$new = (($cur -split ';') | Where-Object { $_ -ne '' -and $_ -ne $dir -and -not $_.StartsWith($home2, [StringComparison]::OrdinalIgnoreCase) }) -join ';'",
241
275
  'if ($new -ne $cur) {',
242
276
  " [Environment]::SetEnvironmentVariable('Path', $new, 'User')",
243
277
  " Write-Output 'removed'",
@@ -406,13 +440,15 @@ function cmdInstall(args) {
406
440
  // Los scripts de instalación van ACTIVADOS a propósito: el binario de electron se
407
441
  // baja en un postinstall. Con --ignore-scripts quedaría instalado y roto.
408
442
  step(`npm install -g --prefix ${mode.NPM_PREFIX} (sin root, la primera vez tarda: baja electron)`);
409
- const res = spawnSync(NPM_CMD, ['install', '-g', '--prefix', mode.NPM_PREFIX, spec], {
410
- stdio: 'inherit',
411
- shell: IS_WIN,
412
- env: npmEnv(),
413
- });
443
+ const res = runNpm(['install', '-g', '--prefix', mode.NPM_PREFIX, spec]);
414
444
  if (res.status !== 0) fail('npm no pudo instalar el paquete (ver el error de arriba).');
415
445
 
446
+ // Se anota con qué node se instaló: la app lo necesita para auto-actualizarse y no
447
+ // puede contar con que el PATH de la sesión gráfica tenga uno.
448
+ if (mode.rememberNodeBinary(process.execPath)) {
449
+ step(`node para las actualizaciones: ${process.execPath}`);
450
+ }
451
+
416
452
  if (!fs.existsSync(mode.INSTALLED_PACKAGE_DIR)) {
417
453
  fail(`npm terminó pero no encuentro ${mode.INSTALLED_PACKAGE_DIR}`);
418
454
  }
@@ -439,6 +475,8 @@ function cmdInstall(args) {
439
475
  if (!noPath) {
440
476
  pathInfo = IS_WIN ? addToPathWindows() : addToPathUnix();
441
477
  if (pathInfo.touched.length) step(`agregado al PATH en: ${pathInfo.touched.join(' ')}`);
478
+ const nodeDir = addNodeToPathWindows();
479
+ if (nodeDir) step(`agregado al PATH: ${nodeDir} (lo necesita el comando glassnote)`);
442
480
  }
443
481
 
444
482
  log('');
@@ -487,18 +525,26 @@ function cmdUpdate(args) {
487
525
  const waitIdx = args.indexOf('--wait');
488
526
  const waitMs = waitIdx !== -1 ? parseInt(args[waitIdx + 1], 10) || 0 : 0;
489
527
  const relaunch = args.includes('--relaunch');
490
- const positional = args.filter((a, i) => !a.startsWith('-') && i !== waitIdx + 1);
528
+ // Sin `--wait`, waitIdx es -1 y waitIdx+1 es 0: el filtro se comía el PRIMER
529
+ // posicional, así que `glassnote update 2.4.3` instalaba latest en vez de 2.4.3.
530
+ const positional = args.filter(
531
+ (a, i) => !a.startsWith('-') && !(waitIdx !== -1 && i === waitIdx + 1)
532
+ );
491
533
  const version = positional[0] || 'latest';
492
534
 
493
535
  if (waitMs > 0) sleepSync(waitMs);
494
536
 
537
+ // El cwd no puede estar dentro de lo que npm va a borrar: en Windows un directorio
538
+ // que es el cwd de un proceso vivo no se puede eliminar, y ese proceso es este.
539
+ try {
540
+ process.chdir(os.tmpdir());
541
+ } catch (error) {
542
+ /* si no se puede, npm igual lo intenta */
543
+ }
544
+
495
545
  const spec = toSpec(version);
496
546
  log(`glassnote — actualizando a ${spec}`);
497
- const res = spawnSync(NPM_CMD, ['install', '-g', '--prefix', mode.NPM_PREFIX, spec], {
498
- stdio: 'inherit',
499
- shell: IS_WIN,
500
- env: npmEnv(),
501
- });
547
+ const res = runNpm(['install', '-g', '--prefix', mode.NPM_PREFIX, spec]);
502
548
  if (res.status !== 0) {
503
549
  recordarFalloDeUpdate(version, `npm salió con ${res.status}`);
504
550
  if (relaunch) {
@@ -511,6 +557,10 @@ function cmdUpdate(args) {
511
557
  }
512
558
 
513
559
  olvidarFalloDeUpdate();
560
+ // Igual que en install: si esta actualización la corrió un node de fuera del paquete,
561
+ // se anota para la siguiente. Los clientes instalados antes de que esto existiera no
562
+ // tienen el archivo, y el primer update es su oportunidad de conseguirlo.
563
+ mode.rememberNodeBinary(process.execPath);
514
564
  // Se rehace en cada update: quien ya tenia el cliente instalado de antes no paso
515
565
  // por el install, y el .app tiene que aparecerle igual (y con la version al dia).
516
566
  const appMac = instalarAppMac();
@@ -617,6 +667,11 @@ function cmdStatus() {
617
667
  log(` home: ${mode.HOME_DIR}`);
618
668
  log(` datos: ${userDataDir()}`);
619
669
  log(` autoarranque: ${autoStartOn}`);
670
+ // Sin un node fuera del paquete no hay auto-actualización posible: es lo primero que
671
+ // hay que mirar cuando un cliente se queda clavado en una versión vieja.
672
+ const nodeBinary = mode.resolveNodeBinary(process.execPath);
673
+ log(` node de update: ${nodeBinary || 'NO ENCONTRADO (la auto-actualización va a fallar)'}`);
674
+ log(` npm de update: ${mode.resolveNpmCli(nodeBinary) || `shim del PATH (${NPM_CMD})`}`);
620
675
  if (process.platform === 'linux') {
621
676
  let sandboxState = 'sin electron';
622
677
  try {
package/npmMode.js CHANGED
@@ -115,6 +115,116 @@ function sandboxFlags(electronBinary) {
115
115
  return ['--no-sandbox'];
116
116
  }
117
117
 
118
+ // ── Encontrar un node "de verdad" ──────────────────────────────────────────────────
119
+ //
120
+ // La auto-actualización lanza un ayudante que corre `npm install -g`, y ese ayudante NO
121
+ // puede ser el electron del propio paquete: npm reemplaza justamente ese árbol y en
122
+ // Windows un ejecutable en marcha está BLOQUEADO, así que npm moría con EPERM y la
123
+ // actualización no se aplicaba nunca. (En Linux/macOS borrar un binario en uso sí
124
+ // funciona: por eso el fallo solo se veía en Windows.) Hace falta un node de fuera.
125
+ //
126
+ // Tampoco vale confiar en el PATH para encontrar `npm`: en Windows el instalador puede
127
+ // bajarse un Node a ~/.glassnote y solo lo mete al PATH de ESA sesión de PowerShell. La
128
+ // app la arranca el autoarranque, con otro entorno, y ahí `npm` no existe. Por eso se
129
+ // busca el binario en disco, se recuerda cuál se usó al instalar, y a npm se lo invoca
130
+ // por su npm-cli.js en vez de por el shim.
131
+ const NODE_EXE = process.platform === 'win32' ? 'node.exe' : 'node';
132
+ const NODE_PATH_FILE = path.join(HOME_DIR, 'node-path');
133
+
134
+ function esArchivo(p) {
135
+ try {
136
+ return !!p && fs.statSync(p).isFile();
137
+ } catch (error) {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ function usableNode(p) {
143
+ if (!p) return false;
144
+ if (path.basename(p).toLowerCase() !== NODE_EXE) return false;
145
+ if (!esArchivo(p)) return false;
146
+ // Lo que esté dentro del paquete es lo que npm va a reemplazar: no sirve.
147
+ if (isInside(p, INSTALLED_PACKAGE_DIR)) return false;
148
+ if (isInside(p, PACKAGE_ROOT)) return false;
149
+ return true;
150
+ }
151
+
152
+ function readNodePathFile() {
153
+ try {
154
+ return fs.readFileSync(NODE_PATH_FILE, 'utf8').trim() || null;
155
+ } catch (error) {
156
+ return null;
157
+ }
158
+ }
159
+
160
+ // El CLI deja constancia del node con el que se instaló: es el único que se sabe seguro
161
+ // que existe en esta máquina, aunque no esté en el PATH de la sesión gráfica.
162
+ function rememberNodeBinary(binary) {
163
+ try {
164
+ if (!usableNode(binary)) return false;
165
+ fs.mkdirSync(HOME_DIR, { recursive: true });
166
+ fs.writeFileSync(NODE_PATH_FILE, realpath(binary), 'utf8');
167
+ return true;
168
+ } catch (error) {
169
+ return false;
170
+ }
171
+ }
172
+
173
+ // install.sh e install.ps1 desempacan el tarball oficial en ~/.glassnote/node-vX.Y.Z-...
174
+ function nodesBootstrapeados() {
175
+ const out = [];
176
+ try {
177
+ for (const entry of fs.readdirSync(HOME_DIR)) {
178
+ if (!entry.startsWith('node-v')) continue;
179
+ out.push(path.join(HOME_DIR, entry, NODE_EXE)); // zip de windows
180
+ out.push(path.join(HOME_DIR, entry, 'bin', NODE_EXE)); // tarball unix
181
+ }
182
+ } catch (error) {
183
+ /* ~/.glassnote puede no existir todavía */
184
+ }
185
+ return out;
186
+ }
187
+
188
+ function nodesEnPath() {
189
+ return String(process.env.PATH || '')
190
+ .split(path.delimiter)
191
+ .filter(Boolean)
192
+ .map((dir) => path.join(dir, NODE_EXE));
193
+ }
194
+
195
+ // `preferido` es el ejecutable actual: cuando el CLI corre bajo node (npx, el ayudante)
196
+ // ese es el candidato correcto. Cuando corre bajo electron queda descartado solo, por
197
+ // estar dentro del paquete.
198
+ function resolveNodeBinary(preferido) {
199
+ const candidatos = [
200
+ process.env.GLASSNOTE_NODE,
201
+ preferido,
202
+ readNodePathFile(),
203
+ path.join(BIN_DIR, NODE_EXE),
204
+ ]
205
+ .concat(nodesBootstrapeados())
206
+ .concat(nodesEnPath());
207
+ for (const candidato of candidatos) {
208
+ if (usableNode(candidato)) return realpath(candidato);
209
+ }
210
+ return null;
211
+ }
212
+
213
+ // npm-cli.js siempre viaja al lado del node que lo trae; el shim del PATH, no.
214
+ function resolveNpmCli(nodeBinary) {
215
+ if (!nodeBinary) return null;
216
+ const dir = path.dirname(nodeBinary);
217
+ const candidatos = [
218
+ path.join(dir, 'node_modules', 'npm', 'bin', 'npm-cli.js'), // zip de windows
219
+ path.join(dir, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js'), // tarball unix / node del sistema
220
+ path.join(dir, '..', 'node_modules', 'npm', 'bin', 'npm-cli.js'),
221
+ ];
222
+ for (const candidato of candidatos) {
223
+ if (esArchivo(candidato)) return path.normalize(candidato);
224
+ }
225
+ return null;
226
+ }
227
+
118
228
  const isNpmMode = () => detect() === 'npm-global' || detect() === 'npx';
119
229
  const isEphemeral = () => detect() === 'npx';
120
230
  const isGlobalInstall = () => detect() === 'npm-global';
@@ -132,6 +242,10 @@ module.exports = {
132
242
  sandboxIsSetuidRoot,
133
243
  chromeSandboxPath,
134
244
  userNamespacesUsable,
245
+ resolveNodeBinary,
246
+ resolveNpmCli,
247
+ rememberNodeBinary,
248
+ NODE_PATH_FILE,
135
249
  isNpmMode,
136
250
  isEphemeral,
137
251
  isGlobalInstall,
package/npmUpdate.js CHANGED
@@ -7,8 +7,14 @@
7
7
  // se está ejecutando está bloqueado y npm no puede reemplazarlo. Se lanza un ayudante
8
8
  // desprendido, la app se cierra, el ayudante actualiza y la vuelve a abrir. Igual en las
9
9
  // tres plataformas, así no hay una rama por sistema operativo que solo se pruebe en uno.
10
+ //
11
+ // El ayudante corre con un node de FUERA del paquete (npmMode.resolveNodeBinary). Antes
12
+ // se lanzaba con el propio electron en modo node, que vive dentro del árbol que npm va a
13
+ // reemplazar: en Windows eso lo mantenía bloqueado y el update fallaba siempre.
10
14
  const https = require('https');
15
+ const http = require('http');
11
16
  const fs = require('fs');
17
+ const os = require('os');
12
18
  const path = require('path');
13
19
  const { spawn } = require('child_process');
14
20
 
@@ -18,7 +24,7 @@ const packageJson = require('./package.json');
18
24
 
19
25
  const REGISTRY = process.env.GLASSNOTE_REGISTRY || 'https://registry.npmjs.org';
20
26
  const CHECK_DELAY_MS = 5000;
21
- const CHECK_INTERVAL_MS = 3 * 60 * 60 * 1000; // cada 3 horas, como el updater viejo
27
+ const CHECK_INTERVAL_MS = 60 * 60 * 1000; // cada hora
22
28
 
23
29
  class NpmUpdate {
24
30
  constructor() {
@@ -48,8 +54,12 @@ class NpmUpdate {
48
54
  }
49
55
 
50
56
  getJson(url) {
57
+ // El módulo va por protocolo: con `https` fijo, un GLASSNOTE_REGISTRY en http
58
+ // —el registry local de scripts/local-registry.js— moría en ERR_INVALID_PROTOCOL
59
+ // y la variable no servía para probar el auto-update.
60
+ const client = String(url).startsWith('http://') ? http : https;
51
61
  return new Promise((resolve) => {
52
- const req = https.get(url, { headers: { accept: 'application/json' } }, (res) => {
62
+ const req = client.get(url, { headers: { accept: 'application/json' } }, (res) => {
53
63
  if (res.statusCode !== 200) {
54
64
  res.resume();
55
65
  console.error(`[update] HTTP ${res.statusCode} en ${url}`);
@@ -130,13 +140,27 @@ class NpmUpdate {
130
140
  console.log(`[update] ${this.currentVersion} → ${version}: actualizando y reiniciando`);
131
141
 
132
142
  const cli = path.join(npmMode.PACKAGE_ROOT, 'bin', 'glassnote.js');
133
- // electron sabe hacer de node con ELECTRON_RUN_AS_NODE: así el ayudante no
134
- // depende de que haya un `node` en el PATH de la sesión gráfica.
143
+
144
+ // Un node de fuera del paquete es lo que hace que esto funcione en Windows: el
145
+ // ayudante no puede tener abierto ninguno de los archivos que npm va a borrar.
146
+ const nodeBinary = npmMode.resolveNodeBinary(process.execPath);
135
147
  const env = Object.assign({}, process.env, {
136
- ELECTRON_RUN_AS_NODE: '1',
137
148
  GLASSNOTE_INSTALL_MODE: 'npm-global',
138
149
  PATH: `${npmMode.BIN_DIR}${path.delimiter}${process.env.PATH || ''}`,
139
150
  });
151
+ if (nodeBinary) {
152
+ delete env.ELECTRON_RUN_AS_NODE;
153
+ console.log(`[update] ayudante: ${nodeBinary}`);
154
+ } else {
155
+ // Último recurso: electron haciendo de node. En Linux/macOS actualiza igual
156
+ // (borrar un binario en uso está permitido); en Windows lo más probable es
157
+ // que npm no pueda y falle. Queda dicho en el log para que se vea por qué.
158
+ env.ELECTRON_RUN_AS_NODE = '1';
159
+ console.error(
160
+ '[update] no encuentro un node fuera del paquete; uso electron como node ' +
161
+ '(en Windows esto suele hacer fallar la actualización)'
162
+ );
163
+ }
140
164
 
141
165
  // El ayudante corre desprendido y su salida no la veía nadie: cuando npm fallaba,
142
166
  // el único síntoma era la app reiniciándose sola. Va a un archivo al lado de los
@@ -150,10 +174,12 @@ class NpmUpdate {
150
174
  }
151
175
 
152
176
  try {
177
+ // cwd fuera del paquete: en Windows no se puede borrar un directorio que es
178
+ // el cwd de un proceso vivo, y ese proceso sería justamente el actualizador.
153
179
  const child = spawn(
154
- process.execPath,
180
+ nodeBinary || process.execPath,
155
181
  [cli, 'update', version, '--relaunch', '--wait', '4000'],
156
- { detached: true, stdio: ['ignore', salida, salida], env }
182
+ { detached: true, stdio: ['ignore', salida, salida], env, cwd: os.tmpdir() }
157
183
  );
158
184
  child.unref();
159
185
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassnote/client",
3
- "version": "2.4.4",
3
+ "version": "2.4.6",
4
4
  "description": "GlassNote — cliente de escritorio de notas superpuestas. Se instala con npx, sin instaladores nativos ni firmas.",
5
5
  "main": "main.js",
6
6
  "publishConfig": {