@dotrino/identity 0.121.0 → 0.121.2

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.
@@ -37,7 +37,7 @@ export function utf8Length (s) {
37
37
 
38
38
  export const PATHS = ['proxy', 'direct', 'turn', 'webrtc']
39
39
 
40
- const ceros = () => ({ proxy: 0, direct: 0, turn: 0, webrtc: 0 })
40
+ const zeros = () => ({ proxy: 0, direct: 0, turn: 0, webrtc: 0 })
41
41
 
42
42
  export class TrafficStats {
43
43
  constructor () {
@@ -64,7 +64,7 @@ export class TrafficStats {
64
64
  if (!key) return
65
65
  let p = this.peers.get(key)
66
66
  if (!p) {
67
- p = { token: who.token || null, pubkey: who.pubkey || null, bytesIn: ceros(), bytesOut: ceros(), msgsIn: 0, msgsOut: 0, firstAt: Date.now(), lastAt: 0 }
67
+ p = { token: who.token || null, pubkey: who.pubkey || null, bytesIn: zeros(), bytesOut: zeros(), msgsIn: 0, msgsOut: 0, firstAt: Date.now(), lastAt: 0 }
68
68
  this.peers.set(key, p)
69
69
  }
70
70
  if (who.pubkey && !p.pubkey) p.pubkey = who.pubkey
@@ -104,8 +104,8 @@ export async function routeOf (pc) {
104
104
  const local = byId.get(pair.localCandidateId)
105
105
  const remote = byId.get(pair.remoteCandidateId)
106
106
  if (!local && !remote) return null
107
- const tipo = (c) => c && (c.candidateType || c.type)
108
- return tipo(local) === 'relay' || tipo(remote) === 'relay' ? 'turn' : 'direct'
107
+ const typeOf = (c) => c && (c.candidateType || c.type)
108
+ return typeOf(local) === 'relay' || typeOf(remote) === 'relay' ? 'turn' : 'direct'
109
109
  }
110
110
 
111
111
  /**
@@ -121,7 +121,7 @@ function registry () {
121
121
  return globalThis[REGISTRY]
122
122
  }
123
123
 
124
- function avisar () {
124
+ function notify () {
125
125
  if (typeof globalThis.dispatchEvent === 'function' && typeof Event === 'function') {
126
126
  try { globalThis.dispatchEvent(new Event('dotrino-transports')) } catch (_) {}
127
127
  }
@@ -131,11 +131,11 @@ export function registerTransport (client) {
131
131
  const r = registry()
132
132
  if (r.has(client)) return
133
133
  r.add(client)
134
- avisar()
134
+ notify()
135
135
  }
136
136
 
137
137
  export function unregisterTransport (client) {
138
- if (registry().delete(client)) avisar()
138
+ if (registry().delete(client)) notify()
139
139
  }
140
140
 
141
141
  /** Los clientes registrados en esta página (o proceso). */
@@ -48,13 +48,13 @@ const RTC_TAG = '__cc_rtc__'
48
48
  * es el escalón 4, que siempre funciona.
49
49
  */
50
50
  let _PC = null
51
- let _PCBuscado = false
51
+ let _PCLookedUp = false
52
52
 
53
- export function setPeerConnection (impl) { _PC = impl; _PCBuscado = true }
53
+ export function setPeerConnection (impl) { _PC = impl; _PCLookedUp = true }
54
54
 
55
55
  export function resolvePeerConnection () {
56
- if (_PCBuscado) return _PC
57
- _PCBuscado = true
56
+ if (_PCLookedUp) return _PC
57
+ _PCLookedUp = true
58
58
  if (typeof globalThis.RTCPeerConnection === 'function') { _PC = globalThis.RTCPeerConnection; return _PC }
59
59
  return _PC
60
60
  }
@@ -64,15 +64,15 @@ export function resolvePeerConnection () {
64
64
  * llama una vez al arrancar, no en medio de una negociación.
65
65
  */
66
66
  export async function loadNodePeerConnection () {
67
- if (_PCBuscado && _PC) return _PC
68
- if (typeof globalThis.RTCPeerConnection === 'function') { _PC = globalThis.RTCPeerConnection; _PCBuscado = true; return _PC }
69
- for (const nombre of ['@dotrino/webrtc', 'werift']) {
67
+ if (_PCLookedUp && _PC) return _PC
68
+ if (typeof globalThis.RTCPeerConnection === 'function') { _PC = globalThis.RTCPeerConnection; _PCLookedUp = true; return _PC }
69
+ for (const name of ['@dotrino/webrtc', 'werift']) {
70
70
  try {
71
- const w = await import(nombre)
72
- if (typeof w?.RTCPeerConnection === 'function') { _PC = w.RTCPeerConnection; _PCBuscado = true; return _PC }
71
+ const w = await import(name)
72
+ if (typeof w?.RTCPeerConnection === 'function') { _PC = w.RTCPeerConnection; _PCLookedUp = true; return _PC }
73
73
  } catch (_) { /* no está: se prueba el siguiente, y si no, el proxio (escalón 4) */ }
74
74
  }
75
- _PCBuscado = true
75
+ _PCLookedUp = true
76
76
  return _PC
77
77
  }
78
78
 
@@ -1,3 +1,3 @@
1
- Copia vendorizada de @dotrino/roadmap@0.4.30 (dotrino-roadmap/src/{index,manifests}.js).
1
+ Copia vendorizada de @dotrino/roadmap@0.4.32 (dotrino-roadmap/src/{index,manifests}.js).
2
2
  NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
3
3
  El registro de versiones rotas que viaja con cada versión. Importa @dotrino/compat/ranges.
@@ -80,10 +80,10 @@ export function meets (m, { product, peer, version }) {
80
80
  if (!productOf(m, product)) {
81
81
  return { ok: false, reason: `the registry does not know "${product}"` }
82
82
  }
83
- const rango = requirementFor(m, product, peer)
84
- if (rango == null) return { ok: true, reason: '' }
85
- if (satisfies(version, rango)) return { ok: true, reason: '' }
86
- return { ok: false, reason: `${product} needs ${peer} ${rango} and this one is ${version}` }
83
+ const range = requirementFor(m, product, peer)
84
+ if (range == null) return { ok: true, reason: '' }
85
+ if (satisfies(version, range)) return { ok: true, reason: '' }
86
+ return { ok: false, reason: `${product} needs ${peer} ${range} and this one is ${version}` }
87
87
  }
88
88
 
89
89
  /** La lista de rotas del registro, lista para pasarla a `check` de `@dotrino/compat`. */
@@ -8,7 +8,7 @@
8
8
  export const MANIFESTS = Object.freeze({
9
9
  "dotrino": {
10
10
  "manifest": "dotrino",
11
- "seq": 30,
11
+ "seq": 31,
12
12
  "issued": "2026-10-16",
13
13
  "note": "Registro comun de compatibilidad del ecosistema. Se edita a mano en este repo y se consulta con @dotrino/roadmap. Rangos: ver @dotrino/compat/ranges (x.y.z, >= > <= <, «a - b», «*», o una lista que es un O).",
14
14
  "products": {
@@ -22,16 +22,16 @@ export const MANIFESTS = Object.freeze({
22
22
  "env": {
23
23
  "repo": "imdotrino/dotrino-vault",
24
24
  "npm": "@dotrino/env",
25
- "current": "0.90.1",
25
+ "current": "0.91.1",
26
26
  "protocol": 1,
27
27
  "requires": {
28
- "vault": ">=0.90.1"
28
+ "vault": ">=0.91.1"
29
29
  }
30
30
  },
31
31
  "identity": {
32
32
  "repo": "imdotrino/dotrino-identity",
33
33
  "npm": "@dotrino/identity",
34
- "current": "0.119.0",
34
+ "current": "0.120.0",
35
35
  "protocol": 1,
36
36
  "requires": {
37
37
  "proxy-client": ">=0.13.1"
@@ -40,7 +40,7 @@ export const MANIFESTS = Object.freeze({
40
40
  "lobby": {
41
41
  "repo": "imdotrino/dotrino-lobby",
42
42
  "npm": "@dotrino/lobby",
43
- "current": "0.10.2",
43
+ "current": "0.10.4",
44
44
  "protocol": 2,
45
45
  "requires": {
46
46
  "proxy-client": ">=0.22.0",
@@ -85,7 +85,7 @@ export const MANIFESTS = Object.freeze({
85
85
  "notifications": {
86
86
  "repo": "imdotrino/dotrino-notifications",
87
87
  "npm": "@dotrino/notifications",
88
- "current": "0.6.0",
88
+ "current": "0.7.1",
89
89
  "protocol": 2,
90
90
  "requires": {
91
91
  "proxy-client": ">=0.20.0"
@@ -101,7 +101,7 @@ export const MANIFESTS = Object.freeze({
101
101
  "profile": {
102
102
  "repo": "imdotrino/dotrino-profile",
103
103
  "npm": "@dotrino/profile",
104
- "current": "0.20.1",
104
+ "current": "0.20.2",
105
105
  "protocol": 1,
106
106
  "requires": {
107
107
  "identity": ">=0.33.0"
@@ -109,7 +109,7 @@ export const MANIFESTS = Object.freeze({
109
109
  },
110
110
  "proxy": {
111
111
  "repo": "imdotrino/dotrino-proxy",
112
- "current": "1.6.4",
112
+ "current": "1.7.0",
113
113
  "protocol": 2,
114
114
  "requires": {
115
115
  "vault": ">=0.87.0",
@@ -121,7 +121,7 @@ export const MANIFESTS = Object.freeze({
121
121
  "proxy-client": {
122
122
  "repo": "imdotrino/dotrino-proxy-client",
123
123
  "npm": "@dotrino/proxy-client",
124
- "current": "0.36.1",
124
+ "current": "0.38.2",
125
125
  "protocol": 2,
126
126
  "requires": {
127
127
  "identity": ">=0.53.0",
@@ -131,10 +131,10 @@ export const MANIFESTS = Object.freeze({
131
131
  "remote-agent": {
132
132
  "repo": "imdotrino/dotrino-remote-agent",
133
133
  "npm": "@dotrino/remote-agent",
134
- "current": "0.20.1",
134
+ "current": "0.22.1",
135
135
  "protocol": 1,
136
136
  "requires": {
137
- "identity": ">=0.117.2",
137
+ "identity": ">=0.120.0",
138
138
  "vault": ">=0.85.0",
139
139
  "proxy-client": ">=0.36.1",
140
140
  "vaultd": ">=0.154.0"
@@ -163,7 +163,7 @@ export const MANIFESTS = Object.freeze({
163
163
  },
164
164
  "terminal-android": {
165
165
  "repo": "imdotrino/dotrino-terminal",
166
- "current": "0.13.2",
166
+ "current": "0.13.4",
167
167
  "protocol": 1,
168
168
  "requires": {
169
169
  "terminal-agent": ">=0.37.0"
@@ -193,11 +193,11 @@ export const MANIFESTS = Object.freeze({
193
193
  "content": {
194
194
  "repo": "imdotrino/dotrino-content",
195
195
  "npm": "@dotrino/content",
196
- "current": "0.6.0",
196
+ "current": "0.6.6",
197
197
  "protocol": 1,
198
198
  "requires": {
199
- "remote-agent": ">=0.20.1",
200
- "identity": ">=0.117.2",
199
+ "remote-agent": ">=0.22.0",
200
+ "identity": ">=0.120.0",
201
201
  "vault": ">=0.87.0",
202
202
  "proxy-client": ">=0.36.1"
203
203
  }
@@ -205,7 +205,7 @@ export const MANIFESTS = Object.freeze({
205
205
  "sealers": {
206
206
  "repo": "imdotrino/dotrino-sealers",
207
207
  "npm": "@dotrino/sealers",
208
- "current": "0.10.0",
208
+ "current": "0.10.1",
209
209
  "protocol": 1,
210
210
  "requires": {
211
211
  "remote-agent": ">=0.20.1",
@@ -217,7 +217,7 @@ export const MANIFESTS = Object.freeze({
217
217
  "roadmap": {
218
218
  "repo": "imdotrino/dotrino-roadmap",
219
219
  "npm": "@dotrino/roadmap",
220
- "current": "0.4.30",
220
+ "current": "0.4.32",
221
221
  "protocol": 1,
222
222
  "requires": {
223
223
  "compat": "0.4.0+"
@@ -226,7 +226,7 @@ export const MANIFESTS = Object.freeze({
226
226
  "store": {
227
227
  "repo": "imdotrino/dotrino-store",
228
228
  "npm": "@dotrino/store",
229
- "current": "0.12.3",
229
+ "current": "0.12.4",
230
230
  "protocol": 1,
231
231
  "requires": {
232
232
  "identity": ">=0.91.0",
@@ -243,7 +243,7 @@ export const MANIFESTS = Object.freeze({
243
243
  "topbar": {
244
244
  "repo": "imdotrino/dotrino-topbar",
245
245
  "npm": "@dotrino/topbar",
246
- "current": "0.16.1",
246
+ "current": "0.16.2",
247
247
  "protocol": 1,
248
248
  "requires": {
249
249
  "identity": ">=0.96.1",
@@ -253,11 +253,11 @@ export const MANIFESTS = Object.freeze({
253
253
  "vault": {
254
254
  "repo": "imdotrino/dotrino-vault",
255
255
  "npm": "@dotrino/vault",
256
- "current": "0.90.1",
256
+ "current": "0.91.1",
257
257
  "protocol": 1,
258
258
  "requires": {
259
259
  "vaultd": ">=0.105.0",
260
- "proxy-client": ">=0.36.1",
260
+ "proxy-client": ">=0.38.0",
261
261
  "identity": ">=0.118.0",
262
262
  "opaque": ">=0.1.0",
263
263
  "compat": ">=0.4.0",
@@ -269,11 +269,11 @@ export const MANIFESTS = Object.freeze({
269
269
  "vaultd": {
270
270
  "repo": "imdotrino/dotrino-vault",
271
271
  "npm": "@dotrino/vaultd",
272
- "current": "0.162.1",
272
+ "current": "0.163.4",
273
273
  "protocol": 1,
274
274
  "requires": {
275
275
  "identity": ">=0.118.0",
276
- "proxy-client": ">=0.36.1",
276
+ "proxy-client": ">=0.38.0",
277
277
  "store": ">=0.11.0",
278
278
  "passmanager": ">=0.18.4",
279
279
  "compat": ">=0.4.0",
@@ -284,7 +284,7 @@ export const MANIFESTS = Object.freeze({
284
284
  "verifier": {
285
285
  "repo": "imdotrino/dotrino-verifier",
286
286
  "npm": "@dotrino/verifier",
287
- "current": "0.3.0",
287
+ "current": "0.3.1",
288
288
  "protocol": 1,
289
289
  "requires": {
290
290
  "identity": "0.87.0+"
@@ -293,7 +293,7 @@ export const MANIFESTS = Object.freeze({
293
293
  "passmanager": {
294
294
  "repo": "imdotrino/dotrino-passmanager",
295
295
  "npm": "@dotrino/passmanager",
296
- "current": "0.18.4",
296
+ "current": "0.18.5",
297
297
  "protocol": 1,
298
298
  "requires": {
299
299
  "identity": ">=0.117.2",
@@ -340,6 +340,16 @@ export const MANIFESTS = Object.freeze({
340
340
  "identity": ">=0.110.1"
341
341
  }
342
342
  },
343
+ "app-ios": {
344
+ "repo": "imdotrino/dotrino-app",
345
+ "current": "0.4.13",
346
+ "protocol": 1,
347
+ "requires": {
348
+ "proxy": "1.4.7+",
349
+ "vaultd": ">=0.114.0",
350
+ "identity": ">=0.110.1"
351
+ }
352
+ },
343
353
  "social-bot": {
344
354
  "repo": "imdotrino/dotrino-social-bot",
345
355
  "current": "0.3.0",
@@ -845,6 +855,17 @@ export const MANIFESTS = Object.freeze({
845
855
  ],
846
856
  "why": "la bóveda recordaba el sí de aprobación por aparato: después de aprobar un borrado, o de leer una contraseña, ese aparato podía borrar o pisar una passkey sin que se pidiera nada ni le llegara aviso a nadie",
847
857
  "fix": "sube a @dotrino/vaultd 0.160.2"
858
+ },
859
+ {
860
+ "product": "content",
861
+ "versions": [
862
+ "0.6.1",
863
+ "0.6.2",
864
+ "0.6.3",
865
+ "0.6.4"
866
+ ],
867
+ "why": "el paquete no traía lib/cid.js, que lib/public.js importa: el node no arranca (ERR_MODULE_NOT_FOUND)",
868
+ "fix": "sube a @dotrino/content 0.6.6"
848
869
  }
849
870
  ]
850
871
  }
@@ -1,4 +1,4 @@
1
- Copia vendorizada de @dotrino/vault@0.90.2 (dotrino-vault/lib/src/{admin,b64,browser,core/approvals,core/backup,core/blocked,core/browserDisk,core/commKey,core/disk,core/libVersion,core/machines,core/notices,core/notifiable,core/passwords,core/sealKey,core/sealer,core/sealers,core/secretsStore,core/sha256,core/store,core/subacta,core/threadStore,core/transport,core/vault,enroll,index,loginClient,passwordLogins,procContext,protocol,revocation,sealed}.js).
1
+ Copia vendorizada de @dotrino/vault@0.92.0 (dotrino-vault/lib/src/{admin,b64,browser,core/approvals,core/backup,core/blocked,core/browserDisk,core/commKey,core/disk,core/libVersion,core/machines,core/notices,core/notifiable,core/passwords,core/sealKey,core/sealer,core/sealers,core/secretsStore,core/sha256,core/store,core/subacta,core/threadStore,core/transport,core/vault,enroll,index,loginClient,passwordLogins,procContext,protocol,revocation,sealed}.js).
2
2
  NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
3
3
  browser.js es la bóveda de esta pestaña: el MISMO núcleo que el binario (core/), con
4
4
  el disco en IndexedDB. Importa @dotrino/{compat,roadmap,passmanager,store/core} y
@@ -5,4 +5,4 @@
5
5
  * si no coincide con la del paquete: una versión anunciada que no es la que corre es peor
6
6
  * que no anunciar ninguna.
7
7
  */
8
- export const LIB_VERSION = '0.90.2'
8
+ export const LIB_VERSION = '0.92.0'
@@ -525,14 +525,14 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
525
525
  const [kind, k] = splitOwner(owner)
526
526
  const bag = kind === 'ns' ? data.ns : data.dev
527
527
  const g = (bag[k]?.keyring || []).find((x) => x.gen === gen)
528
- if (!g) throw new Error(`putWrap: no existe la generación ${gen} de ${owner}`)
528
+ if (!g) throw new Error(`putWrap: generation ${gen} of ${owner} does not exist`)
529
529
  if (!wrap?.epk || !wrap?.iv || !wrap?.ct) throw new Error('putWrap: envoltura mal formada')
530
530
  // SOLO AÑADE, NUNCA PISA. Quien re-envuelve es un servicio, y un servicio no
531
531
  // administra: si pudiera reemplazar una envoltura existente podría dejar sin leer
532
532
  // a otro miembro —o a quien administra— con una envoltura basura, y eso es
533
533
  // denegación de servicio disfrazada de reparto. Reemplazar es cosa de la bóveda,
534
534
  // por el camino de escribir (que estrena generación entera).
535
- if (g.wraps?.[memberPub]) throw new Error(`putWrap: ${memberPub.slice(0, 12)}… ya tiene envoltura de la generación ${gen}`)
535
+ if (g.wraps?.[memberPub]) throw new Error(`putWrap: ${memberPub.slice(0, 12)}… already has a wrap for generation ${gen}`)
536
536
  g.wraps = { ...(g.wraps || {}), [memberPub]: wrap }
537
537
  save()
538
538
  return true
@@ -36,7 +36,7 @@ export async function masterPubkeyOf (identity) {
36
36
  * @param {string} [opts.url] URL del proxy (default wss://proxy.dotrino.com).
37
37
  * @returns {Promise<{ client, token:string, identify():Promise<void> }>}
38
38
  */
39
- export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey = null, log = () => {}, makeClient = null, announce = null, recheckMs = 60_000, patienceMs = 5 * 60_000, reannounceMs = 10 * 60_000 }) {
39
+ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey = null, log = () => {}, makeClient = null, announce = null, wakeUrl = null, recheckMs = 60_000, patienceMs = 5 * 60_000, reannounceMs = 10 * 60_000 }) {
40
40
  // Import dinámico DESPUÉS de instalar los globals que el paquete usa.
41
41
  // `WebSocketProxyClient` (la clase) y NO el helper `getWebSocketProxyClient`:
42
42
  // ese es un SINGLETON de proceso, y con multi-perfil el vault necesita una
@@ -182,6 +182,25 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
182
182
  }
183
183
  }
184
184
 
185
+ /**
186
+ * DESPIÉRTAME AQUÍ (una bóveda que no vive conectada, `lambda/`). Si la plataforma da una
187
+ * URL, se registra en el proxio con la llave de comunicación y el acta, para que llame a esa
188
+ * URL cuando llegue algo para la cuenta y no haya nadie suyo conectado. Va pegado a
189
+ * `identify` por lo mismo que el anuncio: el acta puede haber cambiado. Si falla, se dice:
190
+ * lo que se pierde es que la despierten, y eso no se puede callar.
191
+ */
192
+ const registerWake = async () => {
193
+ if (!wakeUrl || !identificado) return
194
+ const comm = commKey?.pub?.() || null
195
+ if (!comm || !actaVista) { log('[vault] cannot register the wake URL: no communication key in the record yet'); return }
196
+ try {
197
+ await client.registerWake({ publicKey: comm, sign: (d) => commKey.sign(d), url: wakeUrl, acta: actaVista, channel: announce || undefined })
198
+ log('[vault] wake URL registered on the proxy')
199
+ } catch (e) {
200
+ log(`[vault] could not register the wake URL (${e.code || ''} ${e.message}) — the proxy will not wake this vault`)
201
+ }
202
+ }
203
+
185
204
  let reintento = null
186
205
  let fallos = 0
187
206
  const identifyWithRetry = async () => {
@@ -189,6 +208,7 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
189
208
  try {
190
209
  await identify()
191
210
  await announceAccount()
211
+ await registerWake()
192
212
  if (fallos) log(`[vault] identified on the proxy after ${fallos + 1} attempt(s)`)
193
213
  fallos = 0
194
214
  } catch (e) {
@@ -80,6 +80,8 @@ const MAX_REPLY_BYTES = 768 * 1024
80
80
  * readProcContext(pid) lo que el sistema dice de un proceso; `null` si no se puede mirar
81
81
  * openLocalDesk({ name, onMessage, channels, maxBytes, log }) el atajo en la misma máquina
82
82
  * makeClient quién crea el cliente del proxio (opcional)
83
+ * wakeUrl la URL que despierta a esta bóveda si no vive conectada (opcional, `lambda/`)
84
+ * onActivity() llega un mensaje del proxio (opcional; la Lambda mide con esto su inactividad)
83
85
  * noticeGroupMs cuánto se agrupan los avisos de cambio
84
86
  * opaque el servidor de OPAQUE, si no vale el del paquete (opcional)
85
87
  * servePasswords `false`: el almacén de contraseñas lo atiende quien monta la bóveda,
@@ -492,7 +494,7 @@ export async function startVault ({ platform, proxyUrl, log = console.log, onEnr
492
494
  // ninguna llave y solo sabe la dirección `nombre@AB12-CD34-EF56`— pueda encontrar a quien
493
495
  // la atiende. Lo construye la pieza compartida, porque quien lo busca es la extensión.
494
496
  const { client, identify: reidentificar, isIdentified } = await createTransport({
495
- identity, url: proxyUrl, commKey, log, announce: vaultChannel(fp), makeClient: platform.makeClient || null
497
+ identity, url: proxyUrl, commKey, log, announce: vaultChannel(fp), makeClient: platform.makeClient || null, wakeUrl: platform.wakeUrl || null
496
498
  })
497
499
 
498
500
  // El registro público de cadenas de selladores: deposita, si hay a dónde, los eslabones
@@ -2189,7 +2191,12 @@ export async function startVault ({ platform, proxyUrl, log = console.log, onEnr
2189
2191
  reply(from, { type: MSG.INCIDENT_RESULT, id: pend.id, approvers, blocked: false })
2190
2192
  }
2191
2193
 
2192
- client.on('message', (from, payload) => onMessage(from, payload))
2194
+ client.on('message', (from, payload) => {
2195
+ // Quien monta la bóveda puede querer saber que hay trabajo (la Lambda se apaga cuando
2196
+ // deja de haberlo). Es un aviso, no decide nada.
2197
+ try { platform.onActivity?.() } catch (_) {}
2198
+ onMessage(from, payload)
2199
+ })
2193
2200
 
2194
2201
  // EL MOSTRADOR LOCAL, siempre que se pueda. Si no se puede abrir (un sistema sin sockets
2195
2202
  // de archivo, un directorio de solo lectura) se dice y se sigue: los servicios usan el