@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.
- package/autoupdate_renderer.js +41 -5
- package/bin/glassnote.js +20 -3
- package/main.js +111 -17
- package/npmUpdate.js +78 -13
- package/package.json +3 -2
package/autoupdate_renderer.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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(`
|
|
279
|
+
console.error(`[renderer] fallo al bajar ${version} (intento ${retryCount}/${maxRetries + 1}):`, error.message);
|
|
244
280
|
|
|
245
281
|
if (retryCount > maxRetries) {
|
|
246
|
-
console.error(
|
|
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
|
-
|
|
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(`
|
|
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
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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"
|
|
775
|
-
//
|
|
776
|
-
//
|
|
777
|
-
//
|
|
778
|
-
//
|
|
779
|
-
//
|
|
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
|
-
//
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
//
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
|
167
|
-
//
|
|
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(
|
|
171
|
-
salida = fs.openSync(
|
|
234
|
+
fs.mkdirSync(path.dirname(logDelDia), { recursive: true });
|
|
235
|
+
salida = fs.openSync(logDelDia, 'a');
|
|
172
236
|
} catch (error) {
|
|
173
|
-
console.error(
|
|
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.
|
|
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.
|
|
78
|
+
"@glassnote/renderer": "2.2.1",
|
|
78
79
|
"cors": "^2.8.5",
|
|
79
80
|
"electron": "^29.4.6",
|
|
80
81
|
"express": "^4.18.2",
|