@dotrino/vaultd 0.60.0 → 0.61.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vaultd",
3
- "version": "0.60.0",
3
+ "version": "0.61.0",
4
4
  "type": "module",
5
5
  "description": "Certificador personal de Dotrino: daemon headless que custodia la clave maestra y delega capacidades a tus dispositivos por el proxy. Tu CA propia.",
6
6
  "bin": {
package/src/daemon.js CHANGED
@@ -340,6 +340,11 @@ export async function runDaemon () {
340
340
  finally { wipe(ak) }
341
341
  return { done: 'perfil desbloqueado' + note, autoLockMs: mgr.profiles.autoLockMs }
342
342
  }
343
+ // TECLEAR EN LA PANTALLA DE CONTROL CUENTA COMO USO. Sin esto, navegar por ella no
344
+ // tocaba el daemon —solo lo hacían las acciones—, así que el candado se cerraba a
345
+ // los 5 minutos de ABRIRLO aunque estuvieras delante trabajando. `ref()` ya estira
346
+ // el plazo; esta op no hace nada más, y por eso no devuelve ningún volcado.
347
+ case 'touch': { ref(); return { done: 'ok' } }
343
348
  case 'lock': { mgr.profiles.lock(ref()); return { done: 'perfil bloqueado' } }
344
349
  // PONER contraseña: los secretos pasan de abrirse con la llave de la máquina a
345
350
  // abrirse con la frase. Hay que volver a cerrar la copia maestra con la nueva, o
package/src/tui/app.js CHANGED
@@ -552,6 +552,28 @@ function autoLockWakeIn (st, now = Date.now()) {
552
552
  * reabriría sola en cuanto alguien pulsara Enter (ver `reunlockSilently`), así que el
553
553
  * candado del daemon no habría cerrado nada.
554
554
  */
555
+ /** Cada cuánto se le dice al daemon «sigo aquí». Muy por debajo del plazo del candado. */
556
+ const TOUCH_MS = 60_000
557
+ let ultimoToque = 0
558
+
559
+ /**
560
+ * Le dice al daemon que sigues delante, sin pedirle nada. Se llama al teclear.
561
+ *
562
+ * No se espera (`await`): estirar el candado no puede meterse en medio de lo que estabas
563
+ * haciendo, y si se pierde un aviso, el siguiente llega un minuto después — con cinco de
564
+ * plazo, no pasa nada.
565
+ */
566
+ function seguirAqui (st, api = vc) {
567
+ const ahora = Date.now()
568
+ if (ahora - ultimoToque < TOUCH_MS) return
569
+ const p = (st.state?.profiles || []).find((x) => x.current)
570
+ // Solo tiene sentido en un perfil con contraseña y abierto: sin candado no hay plazo
571
+ // que estirar, y cerrado no hay nada que mantener abierto.
572
+ if (!p?.protected || p.locked) return
573
+ ultimoToque = ahora
574
+ try { Promise.resolve(api.touchProfile(p.id)).catch(() => {}) } catch (_) {}
575
+ }
576
+
555
577
  async function forgetAutoLocked (term, st, api = vc) {
556
578
  const ids = autoLockedIds(st)
557
579
  if (!ids.length) return false
@@ -1743,6 +1765,13 @@ export async function runTui () {
1743
1765
  if (st.confirm) { await onConfirmKey(st, key); continue }
1744
1766
  if (key.name === 'ctrl-c') { running = false; continue }
1745
1767
 
1768
+ // TECLEAR ES USO. El candado se cierra a los 5 min de no usarse, y hasta ahora
1769
+ // «usar» solo contaba cuando algo pedía al daemon: podías estar media hora
1770
+ // navegando esta pantalla y se cerraba igual, que es lo contrario de lo que se
1771
+ // quería. Se avisa como mucho una vez por minuto —el plazo son cinco, así que
1772
+ // sobra— para no escribir un archivo por tecla.
1773
+ seguirAqui(st)
1774
+
1746
1775
  const ch = key.name === 'char' ? key.ch.toLowerCase() : null
1747
1776
  // 'q' global sale.
1748
1777
  if (ch === 'q') { running = false; continue }
@@ -1785,4 +1814,4 @@ export async function runTui () {
1785
1814
  }
1786
1815
 
1787
1816
  // Solo para pruebas headless (render sin terminal real). No usar en runtime.
1788
- export const __test = { render, activeLocked, autoLockedIds, autoLockWakeIn, forgetAutoLocked, autoLockMin, refreshAll, ensureUnlocked, profileRows, deviceRows, secretRows, devVarRows, meRows, capsRows, pairModeRows, pairingBody, scrollBody, fitHelp, toggleLang, mergeMembersAndCerts }
1817
+ export const __test = { render, activeLocked, autoLockedIds, autoLockWakeIn, forgetAutoLocked, autoLockMin, refreshAll, ensureUnlocked, profileRows, deviceRows, secretRows, devVarRows, meRows, capsRows, pairModeRows, pairingBody, scrollBody, fitHelp, toggleLang, mergeMembersAndCerts, seguirAqui, resetToque: () => { ultimoToque = 0 } }
@@ -224,6 +224,15 @@ export const renameProfile = (profile, name) => profileOp('rename', { profile, n
224
224
  export const removeProfile = (profile) => profileOp('rm', { profile })
225
225
  export const unlockProfile = (profile, password) => profileOp('unlock', { profile, password })
226
226
  export const lockProfile = (profile) => profileOp('lock', { profile })
227
+ /**
228
+ * «Sigo aquí»: estira el plazo del bloqueo automático sin hacer nada más.
229
+ *
230
+ * La pantalla de control la usa al teclear. Sin esto, moverse por ella no tocaba el
231
+ * daemon —solo lo hacían las acciones—, así que el candado se cerraba a los 5 minutos de
232
+ * ABRIRLO aunque estuvieras delante. El plazo se cuenta desde el último uso, y teclear es
233
+ * uso.
234
+ */
235
+ export const touchProfile = (profile) => profileOp('touch', { profile })
227
236
  // `current`: la contraseña que YA tenía el perfil. Hace falta para abrir la copia
228
237
  // maestra de los secretos y volver a cerrarla con la nueva — sin ese paso, cambiar la
229
238
  // contraseña dejaría las variables privadas ilegibles.