@dotrino/remote-agent 0.17.0 → 0.18.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/agent.js +30 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/remote-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Middleware compartido del ecosistema Dotrino para apps de agente remoto cifrado punto a punto (handshake E2E, emparejamiento SAS con vault, revocación, descubrimiento). Lo consumen dotrino-terminal (PTY) y dotrino-ia (chat con IA).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
package/src/agent.js
CHANGED
|
@@ -32,12 +32,13 @@ import { loadLink, saveLink, dataDir } from './link.js'
|
|
|
32
32
|
|
|
33
33
|
/** Sesión cifrada con un dispositivo cliente. La app recibe esto en `onSession`. */
|
|
34
34
|
class AgentSession {
|
|
35
|
-
constructor ({ sid, key, from, device, send }) {
|
|
35
|
+
constructor ({ sid, key, from, device, send, isDirect = null }) {
|
|
36
36
|
this.sid = sid
|
|
37
37
|
this.key = key
|
|
38
38
|
this.from = from // token del cliente en el proxy
|
|
39
39
|
this.device = device // pubkey del dispositivo cliente
|
|
40
40
|
this._send = send // (to, obj) => void
|
|
41
|
+
this._isDirect = isDirect // (token) => boolean, o null si el transporte no lo sabe
|
|
41
42
|
this._h = { message: [], close: [] }
|
|
42
43
|
this._closed = false
|
|
43
44
|
}
|
|
@@ -49,6 +50,33 @@ class AgentSession {
|
|
|
49
50
|
if (env) this._send(this.from, { type: DATA, sid: this.sid, env })
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* ¿VA YA POR EL CANAL DIRECTO (WebRTC) lo que se le mande a este cliente? Mientras sea
|
|
55
|
+
* `false`, cada `send` da la vuelta por el proxio. Sirve para que la app decida qué manda
|
|
56
|
+
* YA y qué puede esperar: nunca para dejar de mandar (subir de escalón no bloquea).
|
|
57
|
+
*/
|
|
58
|
+
direct () {
|
|
59
|
+
if (this._closed || !this._isDirect) return false
|
|
60
|
+
try { return !!this._isDirect(this.from) } catch (_) { return false }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Espera a que abra el canal directo, como mucho `ms`. Resuelve `true` si abrió y `false`
|
|
65
|
+
* si se acabó el plazo o la sesión se cerró — en los dos casos se sigue, por donde haya.
|
|
66
|
+
*/
|
|
67
|
+
whenDirect (ms = 8000) {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
const until = Date.now() + ms
|
|
70
|
+
const look = () => {
|
|
71
|
+
if (this.direct()) return resolve(true)
|
|
72
|
+
if (this._closed || Date.now() >= until) return resolve(false)
|
|
73
|
+
const t = setTimeout(look, 100)
|
|
74
|
+
t.unref?.()
|
|
75
|
+
}
|
|
76
|
+
look()
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
on (ev, cb) { this._h[ev]?.push(cb); return this }
|
|
53
81
|
|
|
54
82
|
async _ingest (env) {
|
|
@@ -375,7 +403,7 @@ export async function startRemoteAgent (opts = {}) {
|
|
|
375
403
|
const ack = { op: ACK, sid, seph: eph.pub, ceph: data.eph, machine: myPub, publickey: myPub, ts: Date.now() }
|
|
376
404
|
const { signature: ackSig } = await signWithDevice({ privateJwk: link.device.privateJwk, data: ack })
|
|
377
405
|
|
|
378
|
-
const session = new AgentSession({ sid, key, from, device: chk.device, send })
|
|
406
|
+
const session = new AgentSession({ sid, key, from, device: chk.device, send, isDirect: typeof client.isWebRTCOpen === 'function' ? (t) => client.isWebRTCOpen(t) : null })
|
|
379
407
|
session._exp = Date.now() + SESSION_TTL_MS
|
|
380
408
|
sessions.set(sid, session)
|
|
381
409
|
audit('session-open', { sid: sid.slice(0, 8), device: (await pubkeyId(chk.device)).slice(0, 8).toUpperCase() })
|