@dotrino/vaultd 0.15.0 → 0.16.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 +1 -1
- package/src/ctl.js +5 -11
- package/src/daemon.js +2 -10
- package/src/tui/app.js +3 -20
- package/src/tui/i18n.js +2 -12
- package/src/vaultControl.js +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vaultd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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/ctl.js
CHANGED
|
@@ -257,18 +257,14 @@ function cmdReject (deviceId) {
|
|
|
257
257
|
* Distinto de `members` (quién es del perfil) y de `profile` (los perfiles del PC): esto
|
|
258
258
|
* es el CONTENIDO, no la identidad.
|
|
259
259
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
260
|
+
* De la foto solo se dice que la hay, de qué tipo y cuánto pesa: es un data-URI de hasta
|
|
261
|
+
* ~90 KB y una terminal no es sitio para volcarlo.
|
|
262
262
|
*/
|
|
263
|
-
async function cmdMe (
|
|
264
|
-
const i = args.findIndex((a) => a === '--foto' || a === '--photo')
|
|
265
|
-
const avatarPath = i >= 0 ? args[i + 1] : null
|
|
266
|
-
if (i >= 0 && !avatarPath) { console.error('uso: dotrino-vault me --foto <archivo>'); process.exit(2) }
|
|
267
|
-
|
|
263
|
+
async function cmdMe () {
|
|
268
264
|
const s = requireDaemon()
|
|
269
265
|
const meFile = path.join(dir, 'me.json')
|
|
270
266
|
try { fs.rmSync(meFile, { force: true }) } catch (_) {}
|
|
271
|
-
writeReq('me-request.json', {
|
|
267
|
+
writeReq('me-request.json', {})
|
|
272
268
|
avisar(s.pid, 'SIGUSR2')
|
|
273
269
|
let dump = null
|
|
274
270
|
for (let n = 0; n < 50; n++) { await sleep(100); const d = readJson(meFile, null); if (d?.at) { dump = d; break } }
|
|
@@ -306,8 +302,6 @@ async function cmdMe (args = []) {
|
|
|
306
302
|
for (const x of lista) console.log(' %s %s%s', (x.type || x.label || '').padEnd(12), x.value, x.visible === false ? ' (oculto)' : '')
|
|
307
303
|
}
|
|
308
304
|
|
|
309
|
-
if (dump.avatarGuardada) console.log('\n Foto escrita en: %s', dump.avatarGuardada)
|
|
310
|
-
else if (me.avatar) console.log('\n Para verla: dotrino-vault me --foto ~/perfil.png')
|
|
311
305
|
console.log('')
|
|
312
306
|
}
|
|
313
307
|
|
|
@@ -691,7 +685,7 @@ export async function runCtl (argv) {
|
|
|
691
685
|
case 'approve': return cmdApprove(rest[0])
|
|
692
686
|
case 'reject': return cmdReject(rest[0])
|
|
693
687
|
case 'devices': return cmdDevices()
|
|
694
|
-
case 'me': return cmdMe(
|
|
688
|
+
case 'me': return cmdMe()
|
|
695
689
|
case 'members': return cmdMembers()
|
|
696
690
|
case 'caps': return cmdCaps(rest)
|
|
697
691
|
case 'revoke': return cmdRevoke(rest[0])
|
package/src/daemon.js
CHANGED
|
@@ -257,7 +257,7 @@ export async function runDaemon () {
|
|
|
257
257
|
// Solo se vuelca cuando se PIDE, no en cada señal: es contenido del usuario y no tiene
|
|
258
258
|
// por qué quedar escrito en un archivo suelto cada vez que alguien mira los miembros.
|
|
259
259
|
// La FOTO no entra en el volcado (son hasta ~90 KB de data-URI que nadie va a leer en
|
|
260
|
-
// una terminal): se
|
|
260
|
+
// una terminal): solo se dice que la hay, de qué tipo y cuánto pesa.
|
|
261
261
|
const meReq = readJsonSafe(meReqFile)
|
|
262
262
|
if (meReq) {
|
|
263
263
|
rm(meReqFile)
|
|
@@ -265,15 +265,7 @@ export async function runDaemon () {
|
|
|
265
265
|
const tm = resolveTarget(meReq) || { id: mgr.currentId(), vault: mgr.current() }
|
|
266
266
|
const { me } = tm.vault.threads.methods.profileGet()
|
|
267
267
|
const { avatar, ...resto } = me || {}
|
|
268
|
-
|
|
269
|
-
if (meReq.avatarPath && typeof avatar === 'string') {
|
|
270
|
-
const m = /^data:([^;,]+)?(?:;base64)?,(.*)$/s.exec(avatar)
|
|
271
|
-
if (m) {
|
|
272
|
-
fs.writeFileSync(meReq.avatarPath, Buffer.from(m[2], 'base64'), { mode: 0o600 })
|
|
273
|
-
guardada = meReq.avatarPath
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
writeJson(meFile, { v: 1, at: Date.now(), profile: tm.id, me: me ? { ...resto, avatar: avatarInfo(avatar) } : null, avatarGuardada: guardada })
|
|
268
|
+
writeJson(meFile, { v: 1, at: Date.now(), profile: tm.id, me: me ? { ...resto, avatar: avatarInfo(avatar) } : null })
|
|
277
269
|
} catch (e) { console.error('[vault] could not dump the profile:', e.message) }
|
|
278
270
|
}
|
|
279
271
|
} catch (e) {
|
package/src/tui/app.js
CHANGED
|
@@ -268,7 +268,6 @@ function meRows (st, t) {
|
|
|
268
268
|
rows.push({ text: t.accent(' ▸ ' + titulo), sel: false })
|
|
269
269
|
for (const x of lista) campo(x.type || x.label || '', x.value, x.visible === false)
|
|
270
270
|
}
|
|
271
|
-
if (me.avatar) { rows.push({ text: '', sel: false }); rows.push({ text: t.muted(i.savePhotoHint), sel: false }) }
|
|
272
271
|
return rows
|
|
273
272
|
}
|
|
274
273
|
|
|
@@ -603,27 +602,11 @@ async function onKeyPairing (term, st, key) {
|
|
|
603
602
|
}
|
|
604
603
|
|
|
605
604
|
/**
|
|
606
|
-
* Teclas del PERFIL: refrescar
|
|
607
|
-
*
|
|
605
|
+
* Teclas del PERFIL: solo refrescar. Nada de editar — el perfil se edita en el dispositivo
|
|
606
|
+
* que usas, no en la máquina donde vive la bóveda.
|
|
608
607
|
*/
|
|
609
608
|
async function onKeyMe (term, st, key) {
|
|
610
|
-
|
|
611
|
-
const ch = key.name === 'char' ? key.ch.toLowerCase() : null
|
|
612
|
-
if (ch === 'r') {
|
|
613
|
-
await refreshMe(term, st)
|
|
614
|
-
} else if (ch === 'f' && st.me?.avatar) {
|
|
615
|
-
setInput(st, {
|
|
616
|
-
label: i.savePhotoLabel,
|
|
617
|
-
hint: i.savePhotoHintInput,
|
|
618
|
-
value: 'perfil.png',
|
|
619
|
-
onSubmit: async (valor) => {
|
|
620
|
-
const destino = String(valor || '').trim()
|
|
621
|
-
if (!destino) return
|
|
622
|
-
const r = await guard(term, st, i.savingPhoto, () => vc.saveAvatar(destino, activeId(st)))
|
|
623
|
-
if (r.ok && r.v) flash(st, i.photoSaved(r.v))
|
|
624
|
-
}
|
|
625
|
-
})
|
|
626
|
-
}
|
|
609
|
+
if (key.name === 'char' && key.ch.toLowerCase() === 'r') await refreshMe(term, st)
|
|
627
610
|
return true
|
|
628
611
|
}
|
|
629
612
|
|
package/src/tui/i18n.js
CHANGED
|
@@ -53,12 +53,7 @@ const es = {
|
|
|
53
53
|
hidden: ' (oculto)',
|
|
54
54
|
noName: '(sin nombre)',
|
|
55
55
|
no: 'no',
|
|
56
|
-
|
|
57
|
-
savePhotoLabel: '¿Dónde guardo la foto?',
|
|
58
|
-
savePhotoHintInput: 'ruta del archivo (Esc cancela)',
|
|
59
|
-
savingPhoto: 'Guardando la foto…',
|
|
60
|
-
photoSaved: (ruta) => `Foto guardada en ${ruta}`,
|
|
61
|
-
helpMe: ['←→ pestaña', 'r refrescar', 'f guardar foto', 'Esc bóvedas', 'l English', 'q salir'],
|
|
56
|
+
helpMe: ['←→ pestaña', 'r refrescar', 'Esc bóvedas', 'l English', 'q salir'],
|
|
62
57
|
tabsHint: ' (←→ cambiar)',
|
|
63
58
|
titleProfiles: 'Bóvedas',
|
|
64
59
|
titlePairing: 'Emparejar un dispositivo',
|
|
@@ -245,12 +240,7 @@ const en = {
|
|
|
245
240
|
hidden: ' (hidden)',
|
|
246
241
|
noName: '(no name)',
|
|
247
242
|
no: 'no',
|
|
248
|
-
|
|
249
|
-
savePhotoLabel: 'Where should I save the photo?',
|
|
250
|
-
savePhotoHintInput: 'file path (Esc cancels)',
|
|
251
|
-
savingPhoto: 'Saving the photo…',
|
|
252
|
-
photoSaved: (ruta) => `Photo saved to ${ruta}`,
|
|
253
|
-
helpMe: ['←→ tab', 'r refresh', 'f save photo', 'Esc vaults', 'l Español', 'q quit'],
|
|
243
|
+
helpMe: ['←→ tab', 'r refresh', 'Esc vaults', 'l Español', 'q quit'],
|
|
254
244
|
tabsHint: ' (←→ switch)',
|
|
255
245
|
titleProfiles: 'Vaults',
|
|
256
246
|
titlePairing: 'Pair a device',
|
package/src/vaultControl.js
CHANGED
|
@@ -234,17 +234,6 @@ export async function getMe (profile) {
|
|
|
234
234
|
return d.me || null
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
/** Escribe la foto de perfil en `destino` (para poder mirarla) y devuelve la ruta. */
|
|
238
|
-
export async function saveAvatar (destino, profile) {
|
|
239
|
-
requireAlive()
|
|
240
|
-
rm(F.me)
|
|
241
|
-
writeReq(F.meReq, { avatarPath: destino }, profile)
|
|
242
|
-
signalOrCleanup('SIGUSR2', [F.meReq])
|
|
243
|
-
const d = await waitFor(F.me)
|
|
244
|
-
rm(F.me)
|
|
245
|
-
if (!d) throw coded('the daemon did not reply', 'NO_REPLY')
|
|
246
|
-
return d.avatarGuardada || null
|
|
247
|
-
}
|
|
248
237
|
|
|
249
238
|
// ---------------------------------------------------------------------------
|
|
250
239
|
// Secretos: scopes (namespaces) y variables (claves)
|