@glassnote/client 2.4.13 → 2.4.15

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.
@@ -21,6 +21,7 @@ class AutoUpdateRenderer {
21
21
  this.expectedHash = null;
22
22
  this.updateInterval = null;
23
23
  this.reloadCallback = reloadCallback;
24
+ this.ultimoChequeo = null;
24
25
  }
25
26
 
26
27
  /**
@@ -45,6 +46,11 @@ class AutoUpdateRenderer {
45
46
  * Inicia el sistema de auto-actualización del renderer
46
47
  */
47
48
  start() {
49
+ console.log(
50
+ `[renderer] auto-actualización activa: en uso ${this.currentVersion}, ` +
51
+ `primer chequeo en 10 s y despues cada 60 min contra ${this.S3_BASE_URL}`
52
+ );
53
+
48
54
  // Primera verificación después de 10 segundos
49
55
  setTimeout(() => {
50
56
  this.checkForUpdates();
@@ -67,15 +73,30 @@ class AutoUpdateRenderer {
67
73
  }
68
74
 
69
75
  /**
70
- * Verifica si hay actualizaciones disponibles del renderer
76
+ * Verifica si hay actualizaciones disponibles del renderer.
77
+ *
78
+ * Igual que en npmUpdate: CADA chequeo deja una linea. Antes solo hablaba cuando
79
+ * fallaba, asi que un renderer viejo en un equipo era indistinguible de un chequeo que
80
+ * nunca corrio, y son dos cosas muy distintas. El atraso contra el intervalo delata al
81
+ * equipo que estuvo suspendido, que es la explicacion mas comun de "no se actualizo
82
+ * hasta que reinicie".
71
83
  */
72
84
  async checkForUpdates() {
85
+ const ahora = Date.now();
86
+ const desdeElAnterior = this.ultimoChequeo ? ahora - this.ultimoChequeo : null;
87
+ this.ultimoChequeo = ahora;
88
+ const atraso =
89
+ desdeElAnterior !== null
90
+ ? ` (${Math.round(desdeElAnterior / 60000)} min desde el anterior` +
91
+ `${desdeElAnterior > 90 * 60 * 1000 ? ', MUCHO mas que el intervalo' : ''})`
92
+ : '';
93
+
73
94
  try {
74
95
  const versionUrl = `${this.S3_BASE_URL}/version_renderer.txt`;
75
96
 
76
97
  const versionContent = await this.downloadTextFile(versionUrl);
77
98
  if (!versionContent) {
78
- console.error('Error downloading renderer version file');
99
+ console.error(`[renderer] chequeo${atraso}: el canal no devolvio version_renderer.txt`);
79
100
  return;
80
101
  }
81
102
 
@@ -86,11 +107,20 @@ class AutoUpdateRenderer {
86
107
 
87
108
  // Comparar versiones
88
109
  if (this.compareVersions(this.currentVersion, newVersion) < 0) {
110
+ console.warn(
111
+ `[renderer] chequeo${atraso}: hay ${newVersion} y esta en uso ` +
112
+ `${this.currentVersion}; bajando`
113
+ );
89
114
  await this.downloadRenderer(newVersion);
115
+ } else {
116
+ console.log(
117
+ `[renderer] chequeo${atraso}: ${this.currentVersion} al día ` +
118
+ `(el canal está en ${newVersion})`
119
+ );
90
120
  }
91
121
 
92
122
  } catch (error) {
93
- console.error('Error checking for renderer updates:', error);
123
+ console.error(`[renderer] chequeo${atraso}: error comprobando actualizaciones:`, error.message || error);
94
124
  }
95
125
  }
96
126
 
@@ -231,7 +261,13 @@ class AutoUpdateRenderer {
231
261
  fs.unlinkSync(tempZipPath);
232
262
 
233
263
  // Actualizar versión actual
264
+ const anterior = this.currentVersion;
234
265
  this.currentVersion = version;
266
+
267
+ console.warn(
268
+ `[renderer] ${anterior} → ${version}: descomprimido en ${rendererDir} ` +
269
+ `(${Math.round(rendererBuffer.length / 1024)} KB)`
270
+ );
235
271
 
236
272
  // Notificar al main window para refrescar
237
273
  this.notifyMainWindow();
@@ -240,10 +276,10 @@ class AutoUpdateRenderer {
240
276
 
241
277
  } catch (error) {
242
278
  retryCount++;
243
- console.error(`Error downloading renderer (attempt ${retryCount}/${maxRetries + 1}):`, error.message);
279
+ console.error(`[renderer] fallo al bajar ${version} (intento ${retryCount}/${maxRetries + 1}):`, error.message);
244
280
 
245
281
  if (retryCount > maxRetries) {
246
- console.error('All renderer download attempts failed. Giving up.');
282
+ console.error(`[renderer] ${version} no se pudo bajar en ${maxRetries + 1} intentos; se sigue con ${this.currentVersion}`);
247
283
  // Restaurar backup si existe
248
284
  await this.restoreBackupIfNeeded();
249
285
  throw error;
package/bin/glassnote.js CHANGED
@@ -723,7 +723,20 @@ function cmdUpdate(args) {
723
723
  );
724
724
  const version = positional[0] || 'latest';
725
725
 
726
- if (waitMs > 0) esperarAQueCierre(waitMs);
726
+ // Cuando esto lo lanza la app, su salida va al log DEL DÍA, mezclada con la de la
727
+ // app. Por eso el bloque se abre y se cierra con una banda reconocible y cada renglón
728
+ // propio lleva la hora en el mismo formato que usa main.js: lo que npm escriba en el
729
+ // medio no tiene ni prefijo ni hora, y sin las bandas no se sabría de dónde salió.
730
+ const desde = installedVersion();
731
+ log(`\n[${new Date().toISOString()}] ====== UPDATE: ${desde} → ${version} ======`);
732
+
733
+ if (waitMs > 0) {
734
+ const cerro = esperarAQueCierre(waitMs);
735
+ log(
736
+ `[${new Date().toISOString()}] el cliente ${cerro ? 'se cerró' : 'SIGUE ABIERTO'}` +
737
+ `; npm arranca ahora`
738
+ );
739
+ }
727
740
 
728
741
  // Sin `--wait` esto lo está corriendo una persona a mano, con el cliente abierto: el
729
742
  // mismo EBUSY de Windows que en install, y con el mismo remedio.
@@ -744,10 +757,14 @@ function cmdUpdate(args) {
744
757
  }
745
758
 
746
759
  const spec = toSpec(version);
747
- log(`glassnote — actualizando a ${spec}`);
760
+ log(`[${new Date().toISOString()}] glassnote — actualizando a ${spec}`);
748
761
  const res = runNpm(['install', '-g', '--prefix', mode.NPM_PREFIX, spec]);
749
762
  if (res.status !== 0) {
750
763
  recordarFalloDeUpdate(version, `npm salió con ${res.status}`);
764
+ console.error(
765
+ `[${new Date().toISOString()}] ====== UPDATE FALLIDO: npm salió con ${res.status}, ` +
766
+ `sigue instalada la ${desde} ======`
767
+ );
751
768
  if (relaunch) {
752
769
  // Falló la actualización: al menos el cliente vuelve. Quedarse sin app por
753
770
  // un npm que no pudo con la red es peor que seguir en la versión vieja.
@@ -766,7 +783,7 @@ function cmdUpdate(args) {
766
783
  // por el install, y el .app tiene que aparecerle igual (y con la version al dia).
767
784
  const appMac = instalarAppMac();
768
785
  if (appMac) step(`app actualizada en ${appMac}`);
769
- log(`Actualizado a v${installedVersion()}.`);
786
+ log(`[${new Date().toISOString()}] ====== UPDATE OK: v${installedVersion()} ======`);
770
787
  if (relaunch) {
771
788
  launch({ detach: true });
772
789
  return 0;
package/main.js CHANGED
@@ -54,6 +54,9 @@ let cursorFueraDeLaVentana = false;
54
54
  let passthroughPedidoPorRenderer = true;
55
55
  let ignorandoMouse = true;
56
56
  let isCleaningUp = false;
57
+ // El actualizador del renderer, para poder pedirle un chequeo desde afuera de la funcion
58
+ // que lo crea (al volver de dormir).
59
+ let actualizadorRenderer = null;
57
60
 
58
61
 
59
62
  // Inicializar y pasar datos de usuario
@@ -93,9 +96,22 @@ function cleanupOldLogs() {
93
96
  }
94
97
  }
95
98
 
96
- // Create log file for current session
97
- const currentDate = new Date().toISOString().split('T')[0];
98
- const logFilePath = path.join(logsDir, `glassnote-${currentDate}.log`);
99
+ // El archivo del dia, resuelto EN CADA ESCRITURA y no una sola vez al arrancar. Este
100
+ // cliente se queda prendido semanas: con la ruta calculada al inicio, una sesion que
101
+ // empezaba el martes seguia escribiendo en glassnote-<martes>.log el miercoles y el
102
+ // jueves. Quien pedia "el log de hoy" recibia un archivo vacio y lo de hoy estaba
103
+ // enterrado al final del de tres dias antes.
104
+ let diaDelLog = null;
105
+ let logFilePath = null;
106
+
107
+ function archivoDeLog() {
108
+ const hoy = new Date().toISOString().split('T')[0];
109
+ if (hoy !== diaDelLog) {
110
+ diaDelLog = hoy;
111
+ logFilePath = path.join(logsDir, `glassnote-${hoy}.log`);
112
+ }
113
+ return logFilePath;
114
+ }
99
115
 
100
116
  // Override console.log to write to file
101
117
  const originalConsoleLog = console.log;
@@ -110,7 +126,7 @@ console.log = function (...args) {
110
126
 
111
127
  // Write to file
112
128
  try {
113
- fs.appendFileSync(logFilePath, logMessage, 'utf8');
129
+ fs.appendFileSync(archivoDeLog(), logMessage, 'utf8');
114
130
  } catch (error) {
115
131
  originalConsoleLog.apply(console, ['Error writing to log file:', error]);
116
132
  }
@@ -132,7 +148,7 @@ console.warn = function (...args) {
132
148
  const logMessage = `[${timestamp}] WARN: ${message}\n`;
133
149
 
134
150
  try {
135
- fs.appendFileSync(logFilePath, logMessage, 'utf8');
151
+ fs.appendFileSync(archivoDeLog(), logMessage, 'utf8');
136
152
  } catch (error) {
137
153
  originalConsoleWarn.apply(console, ['Error writing to log file:', error]);
138
154
  }
@@ -193,7 +209,7 @@ console.error = function (...args) {
193
209
 
194
210
  // Write to file
195
211
  try {
196
- fs.appendFileSync(logFilePath, logMessage, 'utf8');
212
+ fs.appendFileSync(archivoDeLog(), logMessage, 'utf8');
197
213
  } catch (error) {
198
214
  originalConsoleError.apply(console, ['Error writing to log file:', error]);
199
215
  }
@@ -265,6 +281,8 @@ app.whenReady().then(() => {
265
281
  const versionReport = new VersionReport();
266
282
  versionReport.start();
267
283
 
284
+ vigilarEnergia(autoUpdate);
285
+
268
286
  avisarSiFaltaCompositor();
269
287
 
270
288
  // Create system tray with platform-appropriate icon
@@ -398,7 +416,7 @@ app.whenReady().then(() => {
398
416
  // Also write directly to log file to ensure it's captured
399
417
  const fileLogMessage = `[${timestamp}] ${logMessage}\n`;
400
418
  try {
401
- fs.appendFileSync(logFilePath, fileLogMessage, 'utf8');
419
+ fs.appendFileSync(archivoDeLog(), fileLogMessage, 'utf8');
402
420
  } catch (error) {
403
421
  console.error('Error writing renderer log to file:', error);
404
422
  }
@@ -478,10 +496,14 @@ app.whenReady().then(() => {
478
496
  // Initialize renderer auto-update after mainWindow is created
479
497
  const autoUpdateRenderer = new AutoUpdateRenderer((version) => {
480
498
  // Recargar la ventana para aplicar los cambios
499
+ console.warn(`[renderer] recargando la ventana para estrenar la ${version}`);
481
500
  setTimeout(() => {
482
501
  reloadMainWindow();
483
502
  }, 1000);
484
503
  });
504
+ // Guardado aparte: al volver de dormir se le pide un chequeo desde vigilarEnergia,
505
+ // que vive fuera de esta funcion.
506
+ actualizadorRenderer = autoUpdateRenderer;
485
507
  autoUpdateRenderer.start();
486
508
 
487
509
  // Cargar el HTML del renderer (priorizar userData, fallback a app)
@@ -750,6 +772,10 @@ function lockChecker() {
750
772
  const SONDEO_CURSOR_MS = 30;
751
773
  // Cada cuanto se repite la posicion aunque el cursor no se haya movido.
752
774
  const REEMISION_QUIETO_MS = 400;
775
+ // Cada cuanto se repone el alwaysOnTop mientras la ventana esta a la vista. Es un
776
+ // SetWindowPos cada tanto: barato al lado de un glass que se queda debajo de todo.
777
+ const REAFIRMAR_ALWAYS_ON_TOP_MS = 4000;
778
+ let ultimaReafirmacion = 0;
753
779
 
754
780
  // El passthrough efectivo sale de dos cosas: lo que pidio el renderer y si el cursor esta
755
781
  // siquiera sobre la ventana.
@@ -771,17 +797,21 @@ const REEMISION_QUIETO_MS = 400;
771
797
  // main, sin tocar lo que el renderer cree: cuando el cursor vuelve a entrar se reaplica
772
798
  // su decision tal cual, y desde ahi los eventos reales —que si llegan con la ventana
773
799
  // clickeable— lo ponen al dia.
774
- // El "siempre encima" se vuelve a pedir en cada show, y no alcanza con pedirlo una vez al
775
- // arrancar. Electron guarda el nivel de z-order como un estado suyo y solo baja a la API
776
- // del sistema cuando ESE estado cambia: si Windows le saca el TOPMOST a la ventana por su
777
- // cuenta, para Electron sigue estando arriba —isAlwaysOnTop() devuelve true— y no vuelve a
778
- // pedirlo nunca. De ahi el false y despues el true: es la unica forma de forzar que se
779
- // reemita.
800
+ // El "siempre encima" hay que REPONERLO, no basta con pedirlo una vez al arrancar.
801
+ //
802
+ // Electron guarda el nivel de z-order como un estado suyo y solo baja a la API del sistema
803
+ // cuando ESE estado cambia. Si Windows le saca el TOPMOST a la ventana por su cuenta, para
804
+ // Electron la ventana sigue arriba —isAlwaysOnTop() devuelve true— y no lo vuelve a pedir
805
+ // nunca. Medido en una VM de Windows 11: quitandole el TOPMOST a mano con SetWindowPos, la
806
+ // ventana se queda abajo indefinidamente (14 s de muestreo, sin recuperarse una sola vez).
807
+ // De ahi el false y despues el true: pedir true sobre true no reemite nada.
780
808
  //
781
- // Antes esto no se notaba porque cada glass entraba con show(), que ademas de mostrar
782
- // ACTIVA la ventana, y activarla la sube igual. Al pasar a showInactive() —para no robarle
783
- // el teclado a lo que estes haciendo— se muestra sin activar, y con eso quedo a la vista
784
- // que el alwaysOnTop no se estaba reponiendo.
809
+ // Se repone en dos momentos, y hacen falta los dos:
810
+ // - en el evento `show`, que es cada glass que llega;
811
+ // - cada REAFIRMAR_ALWAYS_ON_TOP_MS mientras la ventana este visible, porque un mensaje
812
+ // largo se queda puesto horas y ahi no hay ningun show que lo repare. Quien le quita
813
+ // el TOPMOST no se pudo reproducir —ni con alt+tab, ni con una app a pantalla completa,
814
+ // ni con el ciclo ocultar/mostrar— asi que esto no depende de saber quien es.
785
815
  //
786
816
  // En Linux no se toca: ahi el "arriba de todo" es _NET_WM_STATE_ABOVE y quitarlo y ponerlo
787
817
  // invita al gestor de ventanas a recolocar la ventana, que es justo lo que costo domar.
@@ -811,6 +841,14 @@ function iniciarSondeoCursor() {
811
841
  sondeoCursor = setInterval(() => {
812
842
  if (!mainWindow || mainWindow.isDestroyed() || !mainWindow.isVisible()) return;
813
843
 
844
+ // Este tick ya corre solo con la ventana a la vista, que es justo cuando importa
845
+ // que este arriba: es el lugar natural para reponer el alwaysOnTop.
846
+ const ahoraMs = Date.now();
847
+ if (ahoraMs - ultimaReafirmacion >= REAFIRMAR_ALWAYS_ON_TOP_MS) {
848
+ ultimaReafirmacion = ahoraMs;
849
+ reafirmarAlwaysOnTop();
850
+ }
851
+
814
852
  // En X11 screen.getCursorScreenPoint() no sirve: con la ventana ignorando el mouse
815
853
  // Chromium se queda sin eventos y devuelve congelada la ultima posicion que vio
816
854
  // (el porque, medido, esta en linuxCursor.js). Se le pregunta al servidor X. En
@@ -860,6 +898,55 @@ function detenerSondeoCursor() {
860
898
  cursorFueraDeLaVentana = false;
861
899
  }
862
900
 
901
+ // El equipo que duerme, escrito en el log.
902
+ //
903
+ // En los logs de campo una maquina suspendida se ve como un HUECO: ocho horas sin una
904
+ // sola linea, y despues todo sigue como si nada. Desde afuera eso es indistinguible de
905
+ // "la app se colgo", que es justo lo que se sospecha cuando alguien reporta que el
906
+ // cliente no se actualizo hasta reiniciar. Anotando suspend/resume el hueco deja de ser
907
+ // un misterio y se puede decir si los chequeos se saltearon porque el equipo no estaba.
908
+ //
909
+ // Y al volver de dormir se fuerza un chequeo: los relojes de setInterval no corren
910
+ // mientras el equipo duerme, asi que un equipo que se suspende todas las noches llega a
911
+ // la manana con el proximo chequeo mucho mas lejos de lo que dice el intervalo.
912
+ function vigilarEnergia(autoUpdate) {
913
+ let dormidoDesde = null;
914
+
915
+ let powerMonitor;
916
+ try {
917
+ powerMonitor = require('electron').powerMonitor;
918
+ } catch (error) {
919
+ console.error('[energia] no hay powerMonitor:', error.message);
920
+ return;
921
+ }
922
+
923
+ powerMonitor.on('suspend', () => {
924
+ dormidoDesde = Date.now();
925
+ console.warn('[energia] el equipo se suspende');
926
+ });
927
+
928
+ powerMonitor.on('resume', () => {
929
+ const minutos = dormidoDesde ? Math.round((Date.now() - dormidoDesde) / 60000) : null;
930
+ dormidoDesde = null;
931
+ console.warn(
932
+ `[energia] el equipo volvio${minutos !== null ? ` tras ~${minutos} min dormido` : ''}` +
933
+ '; se fuerza un chequeo de actualizacion'
934
+ );
935
+ try {
936
+ if (autoUpdate && typeof autoUpdate.checkForUpdates === 'function') {
937
+ autoUpdate.checkForUpdates();
938
+ }
939
+ if (actualizadorRenderer) actualizadorRenderer.checkForUpdates();
940
+ } catch (error) {
941
+ console.error('[energia] el chequeo al volver fallo:', error.message);
942
+ }
943
+ });
944
+
945
+ powerMonitor.on('shutdown', () => console.warn('[energia] el sistema se esta apagando'));
946
+ powerMonitor.on('lock-screen', () => console.log('[energia] pantalla bloqueada'));
947
+ powerMonitor.on('unlock-screen', () => console.log('[energia] pantalla desbloqueada'));
948
+ }
949
+
863
950
  // Una ventana transparente en X11 necesita un compositor: sin el, el servidor X no sabe
864
951
  // mezclar el ARGB con lo que hay detras y la ventana se pinta OPACA. En un overlay a
865
952
  // pantalla completa eso no es un detalle estetico, es la pantalla entera tapada de blanco.
@@ -996,6 +1083,13 @@ function loadRendererHtml(window, uuid) {
996
1083
  }
997
1084
 
998
1085
  if (elegida) {
1086
+ // Que version del renderer corre este equipo y de donde salio, dicho por el proceso
1087
+ // principal y en el arranque. Antes solo se podia deducir del hash del bundle que
1088
+ // aparecia en las lineas del renderer, que no le dice nada a nadie.
1089
+ console.warn(
1090
+ `[renderer] cargando ${elegida.version} desde ${elegida.nombre} (${elegida.htmlPath}) · ` +
1091
+ fuentes.map((f) => `${f.nombre}=${f.version}`).join(' ')
1092
+ );
999
1093
  window.loadFile(elegida.htmlPath, {
1000
1094
  query: { uuid: uuid, version: packageJson.version }
1001
1095
  });
package/npmUpdate.js CHANGED
@@ -26,11 +26,19 @@ const REGISTRY = process.env.GLASSNOTE_REGISTRY || 'https://registry.npmjs.org';
26
26
  const CHECK_DELAY_MS = 5000;
27
27
  const CHECK_INTERVAL_MS = 60 * 60 * 1000; // cada hora
28
28
 
29
+ // El mismo archivo que usa main.js. Se calcula al vuelo y no se guarda: entre un
30
+ // arranque y una actualización pueden pasar días, y el nombre lleva la fecha.
31
+ function archivoDeLogDelDia() {
32
+ const hoy = new Date().toISOString().split('T')[0];
33
+ return path.join(userData.getUserDataDir(), 'logs', `glassnote-${hoy}.log`);
34
+ }
35
+
29
36
  class NpmUpdate {
30
37
  constructor() {
31
38
  this.currentVersion = packageJson.version;
32
39
  this.updateInterval = null;
33
40
  this.updating = false;
41
+ this.ultimoChequeo = null;
34
42
  }
35
43
 
36
44
  start() {
@@ -42,6 +50,11 @@ class NpmUpdate {
42
50
  return;
43
51
  }
44
52
 
53
+ console.log(
54
+ `[update] auto-actualización activa: v${this.currentVersion}, ` +
55
+ `primer chequeo en ${Math.round(CHECK_DELAY_MS / 1000)} s y despues cada ` +
56
+ `${Math.round(CHECK_INTERVAL_MS / 60000)} min contra ${REGISTRY}`
57
+ );
45
58
  setTimeout(() => this.checkForUpdates(), CHECK_DELAY_MS);
46
59
  this.updateInterval = setInterval(() => this.checkForUpdates(), CHECK_INTERVAL_MS);
47
60
  }
@@ -88,28 +101,68 @@ class NpmUpdate {
88
101
  });
89
102
  }
90
103
 
104
+ // CADA chequeo deja una linea, encuentre algo o no.
105
+ //
106
+ // Antes el camino feliz era mudo: solo se escribia cuando el registry fallaba. Con un
107
+ // cliente que se queda prendido semanas eso significa que "no se actualizo" y "ni
108
+ // siquiera miro" se ven EXACTAMENTE IGUAL en el log —las dos cosas son nada—, y son
109
+ // dos problemas distintos con dos arreglos distintos. Se dice tambien cuanto paso
110
+ // desde el chequeo anterior: los relojes de setInterval no corren mientras el equipo
111
+ // duerme, asi que un atraso grande es la pista de que el equipo estuvo suspendido y
112
+ // no de que el reloj se murio.
91
113
  async checkForUpdates() {
92
- if (this.updating) return;
114
+ if (this.updating) {
115
+ console.log('[update] ya hay una actualización en curso; este chequeo se saltea');
116
+ return;
117
+ }
118
+
119
+ const ahora = Date.now();
120
+ const desdeElAnterior = this.ultimoChequeo ? ahora - this.ultimoChequeo : null;
121
+ this.ultimoChequeo = ahora;
122
+ const atraso =
123
+ desdeElAnterior !== null
124
+ ? ` (${Math.round(desdeElAnterior / 60000)} min desde el anterior` +
125
+ `${desdeElAnterior > CHECK_INTERVAL_MS * 1.5 ? ', MUCHO mas que el intervalo' : ''})`
126
+ : '';
127
+
93
128
  try {
94
129
  // Mismos nombres que el updater viejo, incluida la errata 'lastest', para no
95
130
  // romper los userData que ya están en la calle.
96
131
  const desiredVersion = userData.get('desiredVersion') || 'lastest';
97
- if (!desiredVersion) return;
132
+ if (!desiredVersion) {
133
+ console.log(`[update] chequeo${atraso}: sin desiredVersion, no se mira nada`);
134
+ return;
135
+ }
98
136
 
99
137
  const isLatest = desiredVersion === 'lastest' || desiredVersion === 'latest';
100
138
  const target = isLatest ? 'latest' : desiredVersion;
101
139
  const meta = await this.getJson(`${REGISTRY}/${npmMode.PKG_NAME}/${target}`);
102
- if (!meta || !meta.version) return;
140
+ if (!meta || !meta.version) {
141
+ console.error(
142
+ `[update] chequeo${atraso}: el registry no dijo que version es '${target}'`
143
+ );
144
+ return;
145
+ }
103
146
 
104
147
  const objetivo = meta.version;
105
148
  const hayQueActualizar = isLatest
106
149
  ? compareVersions(this.currentVersion, objetivo) < 0
107
150
  : this.currentVersion !== objetivo;
108
- if (!hayQueActualizar) return;
151
+ if (!hayQueActualizar) {
152
+ console.log(
153
+ `[update] chequeo${atraso}: v${this.currentVersion} al día ` +
154
+ `(el canal '${target}' está en ${objetivo})`
155
+ );
156
+ return;
157
+ }
158
+ console.warn(
159
+ `[update] chequeo${atraso}: hay ${objetivo} y corre ${this.currentVersion} ` +
160
+ `(canal '${target}')`
161
+ );
109
162
  if (this.fallidoHacePoco(objetivo)) return;
110
163
  this.applyUpdate(objetivo);
111
164
  } catch (error) {
112
- console.error('[update] error comprobando actualizaciones:', error.message);
165
+ console.error(`[update] chequeo${atraso}: error comprobando actualizaciones:`, error.message);
113
166
  }
114
167
  }
115
168
 
@@ -126,7 +179,10 @@ class NpmUpdate {
126
179
  const desde = Date.now() - (fallo.at || 0);
127
180
  if (desde >= CHECK_INTERVAL_MS) return false;
128
181
  const minutos = Math.max(1, Math.round((CHECK_INTERVAL_MS - desde) / 60000));
129
- console.log(`[update] ${version} ya falló; no se reintenta hasta dentro de ~${minutos} min`);
182
+ console.warn(
183
+ `[update] ${version} ya falló${fallo.motivo ? ` (${fallo.motivo})` : ''}; ` +
184
+ `no se reintenta hasta dentro de ~${minutos} min`
185
+ );
130
186
  return true;
131
187
  } catch (error) {
132
188
  return false;
@@ -137,7 +193,10 @@ class NpmUpdate {
137
193
  if (this.updating) return;
138
194
  this.updating = true;
139
195
  this.stop();
140
- console.log(`[update] ${this.currentVersion} → ${version}: actualizando y reiniciando`);
196
+ console.warn(
197
+ `[update] ${this.currentVersion} → ${version}: se lanza el ayudante, la app se cierra ` +
198
+ `y vuelve sola. El detalle del npm queda en este mismo log`
199
+ );
141
200
 
142
201
  const cli = path.join(npmMode.PACKAGE_ROOT, 'bin', 'glassnote.js');
143
202
 
@@ -150,7 +209,7 @@ class NpmUpdate {
150
209
  });
151
210
  if (nodeBinary) {
152
211
  delete env.ELECTRON_RUN_AS_NODE;
153
- console.log(`[update] ayudante: ${nodeBinary}`);
212
+ console.log(`[update] ayudante: ${nodeBinary} (cli ${cli})`);
154
213
  } else {
155
214
  // Último recurso: electron haciendo de node. En Linux/macOS actualiza igual
156
215
  // (borrar un binario en uso está permitido); en Windows lo más probable es
@@ -163,14 +222,19 @@ class NpmUpdate {
163
222
  }
164
223
 
165
224
  // El ayudante corre desprendido y su salida no la veía nadie: cuando npm fallaba,
166
- // el único síntoma era la app reiniciándose sola. Va a un archivo al lado de los
167
- // datos de usuario, que es donde alguien lo va a buscar.
225
+ // el único síntoma era la app reiniciándose sola. Va al log DEL DÍA, el mismo que
226
+ // escribe la app, y no a un update.log aparte: la actualización es lo que pasa
227
+ // entre el último renglón de un arranque y el primero del siguiente, y en dos
228
+ // archivos distintos —uno de ellos sin fechas— eso había que reconstruirlo a ojo.
229
+ // Aquí queda en su lugar de la línea de tiempo, y se borra con los demás a los 30
230
+ // días. La app ya cerró para cuando npm escribe, así que no se pisan.
168
231
  let salida = 'ignore';
232
+ const logDelDia = archivoDeLogDelDia();
169
233
  try {
170
- fs.mkdirSync(userData.getUserDataDir(), { recursive: true });
171
- salida = fs.openSync(path.join(userData.getUserDataDir(), 'update.log'), 'a');
234
+ fs.mkdirSync(path.dirname(logDelDia), { recursive: true });
235
+ salida = fs.openSync(logDelDia, 'a');
172
236
  } catch (error) {
173
- console.error('[update] no se pudo abrir update.log:', error.message);
237
+ console.error(`[update] no se pudo abrir ${logDelDia}:`, error.message);
174
238
  }
175
239
 
176
240
  try {
@@ -182,6 +246,7 @@ class NpmUpdate {
182
246
  { detached: true, stdio: ['ignore', salida, salida], env, cwd: os.tmpdir() }
183
247
  );
184
248
  child.unref();
249
+ console.warn(`[update] ayudante lanzado, pid ${child.pid}; la app se cierra en 500 ms`);
185
250
  } catch (error) {
186
251
  console.error('[update] no se pudo lanzar el actualizador:', error.message);
187
252
  this.updating = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassnote/client",
3
- "version": "2.4.13",
3
+ "version": "2.4.15",
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": {
@@ -57,6 +57,7 @@
57
57
  "start-electron": "electron .",
58
58
  "test-update": "node scripts/test-update.js",
59
59
  "test-pairing": "node scripts/test-pairing.js",
60
+ "test-reconexion": "node scripts/test-reconexion.js",
60
61
  "test-cli": "node scripts/test-cli.js",
61
62
  "test": "node scripts/test-cli.js && node scripts/test-pairing.js && node scripts/test-update.js"
62
63
  },
@@ -74,7 +75,7 @@
74
75
  "url": "git+ssh://git@intermarkec/intermarkec/glassnote-electron.git"
75
76
  },
76
77
  "dependencies": {
77
- "@glassnote/renderer": "2.0.12",
78
+ "@glassnote/renderer": "2.2.1",
78
79
  "cors": "^2.8.5",
79
80
  "electron": "^29.4.6",
80
81
  "express": "^4.18.2",