@glassnote/client 2.4.15 → 2.4.16
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 +117 -1
- package/bin/glassnote.js +57 -2
- package/package.json +1 -1
- package/pairing.js +14 -0
package/INSTALACION.md
CHANGED
|
@@ -67,11 +67,18 @@ curl -fsSL https://glassnote.intermark.ec/install.sh | sh -s -- \
|
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
```powershell
|
|
70
|
-
# Windows
|
|
70
|
+
# Windows — con parámetros: `[scriptblock]` es lo que deja pasárselos al script bajado
|
|
71
71
|
& ([scriptblock]::Create((irm https://glassnote.intermark.ec/install.ps1))) `
|
|
72
72
|
-OrgId 55 -ApiKey <clave> -DeviceName 'Caja 01' -UserName 'Santiago'
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
```powershell
|
|
76
|
+
# Windows — con variables de entorno: `irm | iex` no deja pasar parámetros, pero el
|
|
77
|
+
# cliente lee las variables (quedan puestas en esa ventana de PowerShell)
|
|
78
|
+
$env:GLASSNOTE_ORG_ID='55'; $env:GLASSNOTE_API_KEY='<clave>'; $env:GLASSNOTE_DEVICE_NAME='Caja 01'; $env:GLASSNOTE_USER_NAME='Santiago'
|
|
79
|
+
irm https://glassnote.intermark.ec/install.ps1 | iex
|
|
80
|
+
```
|
|
81
|
+
|
|
75
82
|
| bandera | variable de entorno | por defecto |
|
|
76
83
|
|---|---|---|
|
|
77
84
|
| `--org-id` | `GLASSNOTE_ORG_ID` | — (obligatoria) |
|
|
@@ -84,6 +91,115 @@ curl -fsSL https://glassnote.intermark.ec/install.sh | sh -s -- \
|
|
|
84
91
|
Las variables de entorno existen porque un despliegue por GPO o por MDM muchas veces no
|
|
85
92
|
puede pasar argumentos pero sí poner entorno.
|
|
86
93
|
|
|
94
|
+
### SCCM, Intune y GPO: el cliente es por usuario
|
|
95
|
+
|
|
96
|
+
El ejecutable, los datos, el token y el autoarranque viven en el perfil de quien instala.
|
|
97
|
+
Es el diseño: en GlassNote un dispositivo es un usuario en un equipo. Dos usuarios en la
|
|
98
|
+
misma máquina son dos dispositivos en el tablero, cada uno con su `device-id`, y cada
|
|
99
|
+
cliente corre mientras su usuario tiene la sesión abierta.
|
|
100
|
+
|
|
101
|
+
Estas herramientas suelen correr los scripts como **SYSTEM**, y un cliente instalado así
|
|
102
|
+
quedaría en el perfil de SYSTEM, donde nadie lo ve.
|
|
103
|
+
|
|
104
|
+
**Lo primero es pedirle a la herramienta que corra en la sesión del usuario:**
|
|
105
|
+
|
|
106
|
+
| herramienta | cómo |
|
|
107
|
+
|---|---|
|
|
108
|
+
| SCCM, Application | *Installation behavior: Install for user*, desplegada a una colección de usuarios |
|
|
109
|
+
| SCCM, Package/Program | *Program can run: Only when a user is logged on* y *Run mode: Run with user's rights*; sirve con colecciones de equipos |
|
|
110
|
+
| Intune, Win32 app | *Install behavior: User* |
|
|
111
|
+
| Intune, script de PowerShell | *Run this script using the logged on credentials: Yes* |
|
|
112
|
+
| GPO | script de inicio de sesión en *User Configuration*, no de arranque en *Computer Configuration* |
|
|
113
|
+
|
|
114
|
+
**Cuando no se puede** —*Run Scripts* de SCCM corre siempre como SYSTEM, igual que una
|
|
115
|
+
Application con *Install for system*— el instalador lo detecta y no instala en SYSTEM:
|
|
116
|
+
|
|
117
|
+
1. busca al usuario con sesión iniciada (el dueño de `explorer.exe`);
|
|
118
|
+
2. deja el instalador en el `%TEMP%` de ese usuario y lo corre con una tarea programada de
|
|
119
|
+
un solo uso, en su sesión y con sus permisos;
|
|
120
|
+
3. espera a que termine, muestra su salida y sale con su código;
|
|
121
|
+
4. borra la tarea y la carpeta.
|
|
122
|
+
|
|
123
|
+
El comando es el mismo en los dos casos. Las variables de entorno y los parámetros llegan
|
|
124
|
+
a la sesión del usuario; la API key viaja como variable de entorno, nunca en una línea de
|
|
125
|
+
comandos, y el script que la lleva se borra al arrancar.
|
|
126
|
+
|
|
127
|
+
```powershell
|
|
128
|
+
# el mismo script para todo el parque
|
|
129
|
+
$env:GLASSNOTE_ORG_ID = '<id de la organización>'
|
|
130
|
+
$env:GLASSNOTE_API_KEY = '<clave>'
|
|
131
|
+
irm https://glassnote.intermark.ec/install.ps1 | iex
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Sin `GLASSNOTE_DEVICE_NAME` ni `GLASSNOTE_USER_NAME`: cada equipo toma su hostname y el
|
|
135
|
+
usuario de la sesión. Fijos en un despliegue masivo, todos los equipos quedan con el mismo
|
|
136
|
+
nombre.
|
|
137
|
+
|
|
138
|
+
Límites del camino por SYSTEM:
|
|
139
|
+
|
|
140
|
+
- **Sin nadie con sesión iniciada no instala** y sale con código 1. En una Application,
|
|
141
|
+
*Only when a user is logged on*.
|
|
142
|
+
- **Instala para el usuario con sesión**, el primero que encuentra. Otro usuario del mismo
|
|
143
|
+
equipo es otro dispositivo y necesita su propia instalación.
|
|
144
|
+
- ***Run Scripts* corta por timeout** (60 s por defecto) y la primera instalación tarda más
|
|
145
|
+
(baja electron). Si corta, la instalación sigue en la sesión del usuario, pero SCCM no
|
|
146
|
+
ve el resultado. Hay que subir el timeout.
|
|
147
|
+
- El `out.log` de la instalación queda tomado por el cliente, que hereda esa salida, hasta
|
|
148
|
+
que se cierra. La carpeta `glassnote-install-*` se limpia en la siguiente instalación.
|
|
149
|
+
|
|
150
|
+
Para desinstalar, también como SYSTEM, el mismo script con `-Uninstall`:
|
|
151
|
+
|
|
152
|
+
```powershell
|
|
153
|
+
& ([scriptblock]::Create((irm https://glassnote.intermark.ec/install.ps1))) -Uninstall
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Cierra el cliente, quita el autoarranque y el PATH, y borra `%USERPROFILE%\.glassnote`.
|
|
157
|
+
**No toca `%APPDATA%\glassnote`**: la identidad del equipo (`device-id`), la configuración
|
|
158
|
+
y el token se conservan, así que al reinstalar es el mismo dispositivo en el tablero. Si el
|
|
159
|
+
node que corre la desinstalación vive dentro de `.glassnote` (el que bajó el instalador),
|
|
160
|
+
esa carpeta la borra un proceso aparte unos segundos después, porque Windows no deja borrar
|
|
161
|
+
un ejecutable en uso.
|
|
162
|
+
|
|
163
|
+
### Dónde queda cada cosa en Windows
|
|
164
|
+
|
|
165
|
+
| | ruta |
|
|
166
|
+
|---|---|
|
|
167
|
+
| paquete y `electron.exe` | `%USERPROFILE%\.glassnote\npm\node_modules\@glassnote\client` |
|
|
168
|
+
| comando `glassnote` | `%USERPROFILE%\.glassnote\npm\glassnote.cmd` |
|
|
169
|
+
| Node bajado por el instalador, si no había | `%USERPROFILE%\.glassnote\node-v20.18.1-win-x64` |
|
|
170
|
+
| configuración y token | `%APPDATA%\glassnote\.glassnote` |
|
|
171
|
+
| identidad del equipo | `%APPDATA%\glassnote\device-id` |
|
|
172
|
+
| logs: uno por día, se borran a los 30 días | `%APPDATA%\glassnote\logs` |
|
|
173
|
+
| renderer bajado en caliente | `%APPDATA%\glassnote\renderer` |
|
|
174
|
+
| volcados de crash | `%APPDATA%\@glassnote\client\Crashpad` |
|
|
175
|
+
| autoarranque | `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\glassnote.lnk` |
|
|
176
|
+
|
|
177
|
+
### Auto-actualización
|
|
178
|
+
|
|
179
|
+
Las dos corren como el usuario y sin administrador, así que siguen funcionando aunque la
|
|
180
|
+
instalación la haya lanzado SYSTEM.
|
|
181
|
+
|
|
182
|
+
- **Cliente**: cada hora pregunta a `registry.npmjs.org` por la versión `latest`, o por
|
|
183
|
+
`desiredVersion` si está puesta. Si hay otra, lanza un ayudante con un node de fuera
|
|
184
|
+
del paquete, la app se cierra, el ayudante corre `npm install -g --prefix
|
|
185
|
+
%USERPROFILE%\.glassnote\npm` y la vuelve a abrir. La salida va al log del día. Si
|
|
186
|
+
falla, no reintenta hasta el siguiente chequeo.
|
|
187
|
+
- **Renderer**: a los 10 s de arrancar y después cada hora mira `version_renderer.txt` en el
|
|
188
|
+
bucket de S3 y baja el zip a `%APPDATA%\glassnote\renderer`. No toca el binario.
|
|
189
|
+
|
|
190
|
+
Salidas a internet que necesita el equipo: `registry.npmjs.org` (instalar y actualizar),
|
|
191
|
+
`glassnote.s3.us-east-1.amazonaws.com` (renderer), `nodejs.org` (solo si no hay Node) y
|
|
192
|
+
la API.
|
|
193
|
+
|
|
194
|
+
Sin probar, pero a revisar con el área de sistemas del cliente:
|
|
195
|
+
|
|
196
|
+
- `%APPDATA%` es *Roaming*. Con perfiles móviles, `device-id` y el token viajan con el
|
|
197
|
+
usuario: dos equipos podrían presentarse como el mismo.
|
|
198
|
+
- Una política de AppLocker o WDAC que no deje correr ejecutables desde el perfil del
|
|
199
|
+
usuario bloquea `electron.exe` y `node.exe`.
|
|
200
|
+
- Un proxy: npm no toma el proxy de Windows, necesita `HTTPS_PROXY` o
|
|
201
|
+
`npm_config_proxy`.
|
|
202
|
+
|
|
87
203
|
### Otro servidor
|
|
88
204
|
|
|
89
205
|
**El websocket no se elige en el instalador: lo decide la API que atiende el alta.** El
|
package/bin/glassnote.js
CHANGED
|
@@ -257,7 +257,7 @@ let warnedNoSandbox = false;
|
|
|
257
257
|
function launch({ detach = false, extra = [] } = {}) {
|
|
258
258
|
const binary = electronPath();
|
|
259
259
|
const args = launchArgs(binary).concat(extra);
|
|
260
|
-
const env = Object.assign(
|
|
260
|
+
const env = Object.assign(pairing.envWithoutPairing(process.env), {
|
|
261
261
|
// El modo se marca aquí, no se adivina desde el main: sobrevive a symlinks.
|
|
262
262
|
GLASSNOTE_INSTALL_MODE: mode.detect(),
|
|
263
263
|
GLASSNOTE_CLI: path.join(__dirname, 'glassnote.js'),
|
|
@@ -792,8 +792,63 @@ function cmdUpdate(args) {
|
|
|
792
792
|
return 0;
|
|
793
793
|
}
|
|
794
794
|
|
|
795
|
+
// Borra ~/.glassnote. En Windows el node.exe que corre ESTE codigo suele vivir adentro (el
|
|
796
|
+
// instalador lo baja ahi cuando el equipo no tiene Node), y un ejecutable en uso no se puede
|
|
797
|
+
// borrar: rmSync moria con EPERM a mitad y dejaba la carpeta a medias, con el autoarranque
|
|
798
|
+
// ya quitado. Se borra ahora todo lo demas -el paquete incluido, que es lo que mira quien
|
|
799
|
+
// comprueba si esta instalado- y la carpeta de ese node la borra un cmd desprendido cuando
|
|
800
|
+
// este proceso ya termino.
|
|
801
|
+
function removeHomeDir() {
|
|
802
|
+
// Tampoco el cwd puede quedar adentro: Windows no borra el directorio actual de un proceso.
|
|
803
|
+
try {
|
|
804
|
+
process.chdir(os.tmpdir());
|
|
805
|
+
} catch (error) {
|
|
806
|
+
/* se intenta igual */
|
|
807
|
+
}
|
|
808
|
+
const ownExe = path.resolve(process.execPath).toLowerCase();
|
|
809
|
+
let deferred = false;
|
|
810
|
+
let complete = true;
|
|
811
|
+
for (const entry of fs.readdirSync(mode.HOME_DIR)) {
|
|
812
|
+
const entryPath = path.join(mode.HOME_DIR, entry);
|
|
813
|
+
if (IS_WIN && ownExe.startsWith(path.resolve(entryPath).toLowerCase() + path.sep)) {
|
|
814
|
+
deferred = true;
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
819
|
+
} catch (error) {
|
|
820
|
+
complete = false;
|
|
821
|
+
console.error(`glassnote: no pude borrar ${entryPath}: ${error.message}`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (!complete) return false;
|
|
825
|
+
if (!deferred) {
|
|
826
|
+
fs.rmSync(mode.HOME_DIR, { recursive: true, force: true });
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
// ping hace de espera porque funciona sin consola; timeout.exe no.
|
|
830
|
+
spawn(process.env.ComSpec || 'cmd.exe', [`/d /s /c "ping -n 4 127.0.0.1 >nul & rmdir /s /q "${mode.HOME_DIR}""`], {
|
|
831
|
+
detached: true,
|
|
832
|
+
stdio: 'ignore',
|
|
833
|
+
windowsHide: true,
|
|
834
|
+
windowsVerbatimArguments: true,
|
|
835
|
+
}).unref();
|
|
836
|
+
step(`${path.dirname(process.execPath)} está en uso: se borra al terminar este proceso`);
|
|
837
|
+
return true;
|
|
838
|
+
}
|
|
839
|
+
|
|
795
840
|
function cmdUninstall() {
|
|
796
841
|
log('glassnote — desinstalando');
|
|
842
|
+
// En Windows no se pueden borrar los archivos de un proceso vivo: se cierra el cliente
|
|
843
|
+
// antes de tocar nada, igual que en install. Sin esto quedaba abierto y con la carpeta
|
|
844
|
+
// a medio borrar.
|
|
845
|
+
if (!cerrarClienteWindows()) {
|
|
846
|
+
fail(
|
|
847
|
+
'el cliente sigue abierto y Windows no deja borrar sus archivos.\n' +
|
|
848
|
+
' Cerralo desde el ícono de la bandeja (clic derecho → Salir) y volvé a intentar.\n' +
|
|
849
|
+
' No se tocó nada.'
|
|
850
|
+
);
|
|
851
|
+
}
|
|
797
852
|
// Se comprueba ANTES de borrar: después los archivos ya no están, pero el proceso que
|
|
798
853
|
// los tenía abiertos sigue vivo y con su ícono en la bandeja. Decir "Desinstalado" con
|
|
799
854
|
// la app todavía en pantalla es lo que confunde.
|
|
@@ -814,7 +869,7 @@ function cmdUninstall() {
|
|
|
814
869
|
|
|
815
870
|
if (fs.existsSync(mode.HOME_DIR)) {
|
|
816
871
|
step(`borrando ${mode.HOME_DIR}`);
|
|
817
|
-
|
|
872
|
+
if (!removeHomeDir()) fail(`no pude borrar todo ${mode.HOME_DIR} (arriba dice qué quedó).`);
|
|
818
873
|
}
|
|
819
874
|
log('');
|
|
820
875
|
if (seguiaAbierto) {
|
package/package.json
CHANGED
package/pairing.js
CHANGED
|
@@ -318,8 +318,22 @@ function argumentoPosicional(args) {
|
|
|
318
318
|
return undefined;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
// El entorno para la app, sin las variables del alta. La API key de la organizacion solo
|
|
322
|
+
// sirve para darse de alta: heredada, queda semanas en el entorno del cliente y de todo lo
|
|
323
|
+
// que el cliente lance. Windows no distingue mayusculas en los nombres de variable, asi que
|
|
324
|
+
// se comparan sin ellas.
|
|
325
|
+
function envWithoutPairing(env) {
|
|
326
|
+
const pairingNames = OPCIONES.map((o) => o.env.toUpperCase());
|
|
327
|
+
const clean = {};
|
|
328
|
+
for (const key of Object.keys(env || {})) {
|
|
329
|
+
if (!pairingNames.includes(key.toUpperCase())) clean[key] = env[key];
|
|
330
|
+
}
|
|
331
|
+
return clean;
|
|
332
|
+
}
|
|
333
|
+
|
|
321
334
|
module.exports = {
|
|
322
335
|
API_URL_POR_DEFECTO,
|
|
336
|
+
envWithoutPairing,
|
|
323
337
|
BANDERAS_CON_VALOR,
|
|
324
338
|
argumentoPosicional,
|
|
325
339
|
OPCIONES,
|