@agentvault/agentvault 0.9.5 → 0.9.7
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/dist/__tests__/crypto-helpers.test.d.ts +2 -0
- package/dist/__tests__/crypto-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/functional.test.d.ts +21 -0
- package/dist/__tests__/functional.test.d.ts.map +1 -0
- package/dist/__tests__/multi-session.test.d.ts +2 -0
- package/dist/__tests__/multi-session.test.d.ts.map +1 -0
- package/dist/__tests__/state.test.d.ts +2 -0
- package/dist/__tests__/state.test.d.ts.map +1 -0
- package/dist/__tests__/transport.test.d.ts +2 -0
- package/dist/__tests__/transport.test.d.ts.map +1 -0
- package/dist/channel.d.ts +6 -2
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +2257 -0
- package/dist/channel.js.map +1 -0
- package/dist/cli.js +336 -91
- package/dist/cli.js.map +4 -4
- package/dist/crypto-helpers.js +4 -0
- package/dist/crypto-helpers.js.map +1 -0
- package/dist/index.js +313 -91
- package/dist/index.js.map +4 -4
- package/dist/openclaw-entry.js +1 -1
- package/dist/openclaw-entry.js.map +2 -2
- package/dist/openclaw-plugin.js +222 -0
- package/dist/openclaw-plugin.js.map +1 -0
- package/dist/setup.js +329 -0
- package/dist/setup.js.map +1 -0
- package/dist/state.js +61 -0
- package/dist/state.js.map +1 -0
- package/dist/transport.js +43 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Re-export transport utilities from shared @agentvault/crypto package.
|
|
2
|
+
// Plugin code continues importing from ./crypto-helpers.js — no import changes needed.
|
|
3
|
+
export { hexToBytes, bytesToHex, base64ToBytes, bytesToBase64, encryptedMessageToTransport, transportToEncryptedMessage, } from "@agentvault/crypto";
|
|
4
|
+
//# sourceMappingURL=crypto-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto-helpers.js","sourceRoot":"","sources":["../src/crypto-helpers.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uFAAuF;AACvF,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,2BAA2B,EAC3B,2BAA2B,GAE5B,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -73,9 +73,9 @@ if (typeof Module.getRandomValue === "undefined") {
|
|
|
73
73
|
Module.getRandomValue = randomValuesStandard;
|
|
74
74
|
} catch (e) {
|
|
75
75
|
try {
|
|
76
|
-
|
|
76
|
+
crypto2 = null;
|
|
77
77
|
randomValueNodeJS = function() {
|
|
78
|
-
var buf =
|
|
78
|
+
var buf = crypto2["randomBytes"](4);
|
|
79
79
|
return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0;
|
|
80
80
|
};
|
|
81
81
|
randomValueNodeJS();
|
|
@@ -88,7 +88,7 @@ if (typeof Module.getRandomValue === "undefined") {
|
|
|
88
88
|
var window_;
|
|
89
89
|
var crypto_;
|
|
90
90
|
var randomValuesStandard;
|
|
91
|
-
var
|
|
91
|
+
var crypto2;
|
|
92
92
|
var randomValueNodeJS;
|
|
93
93
|
var _Module = Module;
|
|
94
94
|
Module.ready = new Promise(function(resolve2, reject) {
|
|
@@ -40283,7 +40283,7 @@ Module.ready = new Promise(function(resolve2, reject) {
|
|
|
40283
40283
|
try {
|
|
40284
40284
|
var window_ = "object" === typeof window ? window : self;
|
|
40285
40285
|
var crypto_ = typeof window_.crypto !== "undefined" ? window_.crypto : window_.msCrypto;
|
|
40286
|
-
crypto_ = crypto_ === void 0 ?
|
|
40286
|
+
crypto_ = crypto_ === void 0 ? crypto2 : crypto_;
|
|
40287
40287
|
var randomValuesStandard = function() {
|
|
40288
40288
|
var buf = new Uint32Array(1);
|
|
40289
40289
|
crypto_.getRandomValues(buf);
|
|
@@ -40293,9 +40293,9 @@ Module.ready = new Promise(function(resolve2, reject) {
|
|
|
40293
40293
|
Module3.getRandomValue = randomValuesStandard;
|
|
40294
40294
|
} catch (e) {
|
|
40295
40295
|
try {
|
|
40296
|
-
var
|
|
40296
|
+
var crypto2 = null;
|
|
40297
40297
|
var randomValueNodeJS = function() {
|
|
40298
|
-
var buf =
|
|
40298
|
+
var buf = crypto2["randomBytes"](4);
|
|
40299
40299
|
return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0;
|
|
40300
40300
|
};
|
|
40301
40301
|
randomValueNodeJS();
|
|
@@ -41343,7 +41343,7 @@ Module.ready = new Promise(function(resolve2, reject) {
|
|
|
41343
41343
|
try {
|
|
41344
41344
|
var window_ = "object" === typeof window ? window : self;
|
|
41345
41345
|
var crypto_ = typeof window_.crypto !== "undefined" ? window_.crypto : window_.msCrypto;
|
|
41346
|
-
crypto_ = crypto_ === void 0 ?
|
|
41346
|
+
crypto_ = crypto_ === void 0 ? crypto2 : crypto_;
|
|
41347
41347
|
var randomValuesStandard = function() {
|
|
41348
41348
|
var buf = new Uint32Array(1);
|
|
41349
41349
|
crypto_.getRandomValues(buf);
|
|
@@ -41353,9 +41353,9 @@ Module.ready = new Promise(function(resolve2, reject) {
|
|
|
41353
41353
|
Module2.getRandomValue = randomValuesStandard;
|
|
41354
41354
|
} catch (e) {
|
|
41355
41355
|
try {
|
|
41356
|
-
var
|
|
41356
|
+
var crypto2 = null;
|
|
41357
41357
|
var randomValueNodeJS = function() {
|
|
41358
|
-
var buf =
|
|
41358
|
+
var buf = crypto2["randomBytes"](4);
|
|
41359
41359
|
return (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) >>> 0;
|
|
41360
41360
|
};
|
|
41361
41361
|
randomValueNodeJS();
|
|
@@ -44766,6 +44766,22 @@ var DoubleRatchet = class _DoubleRatchet {
|
|
|
44766
44766
|
const currentDhHex = this.state.dhReceivingPublicKey ? libsodium_wrappers_default.to_hex(this.state.dhReceivingPublicKey) : null;
|
|
44767
44767
|
if (currentDhHex === null && this.state.receivingChain) {
|
|
44768
44768
|
this.state.dhReceivingPublicKey = message.header.dhPublicKey;
|
|
44769
|
+
} else if (currentDhHex === null && !this.state.receivingChain) {
|
|
44770
|
+
if (message.header.messageNumber === 0) {
|
|
44771
|
+
try {
|
|
44772
|
+
const { messageKey: testKey, nextChainKey: nextChainKey2 } = kdfChainKey(this.state.rootKey);
|
|
44773
|
+
const ad2 = serializeHeader(message.header);
|
|
44774
|
+
const ptBytes = libsodium_wrappers_default.crypto_aead_xchacha20poly1305_ietf_decrypt(null, message.ciphertext, ad2, message.nonce, testKey);
|
|
44775
|
+
this.state.dhReceivingPublicKey = message.header.dhPublicKey;
|
|
44776
|
+
this.state.receivingChain = {
|
|
44777
|
+
chainKey: nextChainKey2,
|
|
44778
|
+
messageNumber: 1
|
|
44779
|
+
};
|
|
44780
|
+
return libsodium_wrappers_default.to_string(ptBytes);
|
|
44781
|
+
} catch {
|
|
44782
|
+
}
|
|
44783
|
+
}
|
|
44784
|
+
this.dhRatchetReceive(message.header.dhPublicKey);
|
|
44769
44785
|
} else if (headerDhHex !== currentDhHex) {
|
|
44770
44786
|
if (this.state.receivingChain && this.state.dhReceivingPublicKey) {
|
|
44771
44787
|
this.skipMessages(this.state.receivingChain, message.header.previousChainLength, this.state.dhReceivingPublicKey);
|
|
@@ -45149,6 +45165,208 @@ function transportToEncryptedMessage(transport) {
|
|
|
45149
45165
|
};
|
|
45150
45166
|
}
|
|
45151
45167
|
|
|
45168
|
+
// ../crypto/dist/telemetry.js
|
|
45169
|
+
function randomHex(byteCount) {
|
|
45170
|
+
const bytes = new Uint8Array(byteCount);
|
|
45171
|
+
crypto.getRandomValues(bytes);
|
|
45172
|
+
let hex = "";
|
|
45173
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
45174
|
+
hex += bytes[i2].toString(16).padStart(2, "0");
|
|
45175
|
+
}
|
|
45176
|
+
return hex;
|
|
45177
|
+
}
|
|
45178
|
+
function generateTraceId() {
|
|
45179
|
+
return randomHex(16);
|
|
45180
|
+
}
|
|
45181
|
+
function generateSpanId() {
|
|
45182
|
+
return randomHex(8);
|
|
45183
|
+
}
|
|
45184
|
+
function buildLlmSpan(opts) {
|
|
45185
|
+
const now = Date.now();
|
|
45186
|
+
const attributes = {
|
|
45187
|
+
"ai.agent.llm.model": opts.model,
|
|
45188
|
+
"ai.agent.llm.latency_ms": opts.latencyMs,
|
|
45189
|
+
"ai.agent.llm.tokens_input": opts.tokensInput,
|
|
45190
|
+
"ai.agent.llm.tokens_output": opts.tokensOutput
|
|
45191
|
+
};
|
|
45192
|
+
if (opts.provider !== void 0) {
|
|
45193
|
+
attributes["ai.agent.llm.provider"] = opts.provider;
|
|
45194
|
+
}
|
|
45195
|
+
const isError = opts.status === "error";
|
|
45196
|
+
const status = isError ? { code: 2, ...opts.statusMessage ? { message: opts.statusMessage } : {} } : { code: 0 };
|
|
45197
|
+
return {
|
|
45198
|
+
traceId: opts.traceId ?? generateTraceId(),
|
|
45199
|
+
spanId: opts.spanId ?? generateSpanId(),
|
|
45200
|
+
parentSpanId: opts.parentSpanId,
|
|
45201
|
+
name: "llm.inference",
|
|
45202
|
+
kind: "internal",
|
|
45203
|
+
startTime: now - opts.latencyMs,
|
|
45204
|
+
endTime: now,
|
|
45205
|
+
attributes,
|
|
45206
|
+
status
|
|
45207
|
+
};
|
|
45208
|
+
}
|
|
45209
|
+
function buildToolSpan(opts) {
|
|
45210
|
+
const now = Date.now();
|
|
45211
|
+
const attributes = {
|
|
45212
|
+
"ai.agent.tool.name": opts.toolName,
|
|
45213
|
+
"ai.agent.tool.latency_ms": opts.latencyMs,
|
|
45214
|
+
"ai.agent.tool.success": opts.success
|
|
45215
|
+
};
|
|
45216
|
+
const status = opts.success ? { code: 0 } : { code: 2, ...opts.errorMessage ? { message: opts.errorMessage } : {} };
|
|
45217
|
+
return {
|
|
45218
|
+
traceId: opts.traceId ?? generateTraceId(),
|
|
45219
|
+
spanId: opts.spanId ?? generateSpanId(),
|
|
45220
|
+
parentSpanId: opts.parentSpanId,
|
|
45221
|
+
name: "tool.execute",
|
|
45222
|
+
kind: "internal",
|
|
45223
|
+
startTime: now - opts.latencyMs,
|
|
45224
|
+
endTime: now,
|
|
45225
|
+
attributes,
|
|
45226
|
+
status
|
|
45227
|
+
};
|
|
45228
|
+
}
|
|
45229
|
+
function buildErrorSpan(opts) {
|
|
45230
|
+
const now = Date.now();
|
|
45231
|
+
return {
|
|
45232
|
+
traceId: opts.traceId ?? generateTraceId(),
|
|
45233
|
+
spanId: opts.spanId ?? generateSpanId(),
|
|
45234
|
+
parentSpanId: opts.parentSpanId,
|
|
45235
|
+
name: "error",
|
|
45236
|
+
kind: opts.spanKind ?? "internal",
|
|
45237
|
+
startTime: now,
|
|
45238
|
+
endTime: now,
|
|
45239
|
+
attributes: {
|
|
45240
|
+
"ai.agent.error.type": opts.errorType,
|
|
45241
|
+
"ai.agent.error.message": opts.errorMessage
|
|
45242
|
+
},
|
|
45243
|
+
status: { code: 2, message: opts.errorMessage }
|
|
45244
|
+
};
|
|
45245
|
+
}
|
|
45246
|
+
|
|
45247
|
+
// ../crypto/dist/telemetry-reporter.js
|
|
45248
|
+
function toOtlpAttributes(attrs) {
|
|
45249
|
+
return Object.entries(attrs).map(([key, val]) => {
|
|
45250
|
+
if (typeof val === "string") {
|
|
45251
|
+
return { key, value: { stringValue: val } };
|
|
45252
|
+
}
|
|
45253
|
+
if (typeof val === "boolean") {
|
|
45254
|
+
return { key, value: { boolValue: val } };
|
|
45255
|
+
}
|
|
45256
|
+
if (Number.isInteger(val)) {
|
|
45257
|
+
return { key, value: { intValue: val } };
|
|
45258
|
+
}
|
|
45259
|
+
return { key, value: { doubleValue: val } };
|
|
45260
|
+
});
|
|
45261
|
+
}
|
|
45262
|
+
function spanToOtlp(span) {
|
|
45263
|
+
const otlp = {
|
|
45264
|
+
traceId: span.traceId,
|
|
45265
|
+
spanId: span.spanId,
|
|
45266
|
+
name: span.name,
|
|
45267
|
+
kind: span.kind,
|
|
45268
|
+
startTimeUnixNano: String(span.startTime * 1e6),
|
|
45269
|
+
endTimeUnixNano: String(span.endTime * 1e6),
|
|
45270
|
+
attributes: toOtlpAttributes(span.attributes)
|
|
45271
|
+
};
|
|
45272
|
+
if (span.parentSpanId !== void 0) {
|
|
45273
|
+
otlp.parentSpanId = span.parentSpanId;
|
|
45274
|
+
}
|
|
45275
|
+
if (span.status) {
|
|
45276
|
+
otlp.status = span.status;
|
|
45277
|
+
}
|
|
45278
|
+
if (span.events && span.events.length > 0) {
|
|
45279
|
+
otlp.events = span.events;
|
|
45280
|
+
}
|
|
45281
|
+
return otlp;
|
|
45282
|
+
}
|
|
45283
|
+
var TelemetryReporter = class {
|
|
45284
|
+
_apiBase;
|
|
45285
|
+
_hubId;
|
|
45286
|
+
_authHeader;
|
|
45287
|
+
_fetch;
|
|
45288
|
+
_buffer = [];
|
|
45289
|
+
_timer = null;
|
|
45290
|
+
constructor(config) {
|
|
45291
|
+
this._apiBase = config.apiBase.replace(/\/+$/, "");
|
|
45292
|
+
this._hubId = config.hubId;
|
|
45293
|
+
this._authHeader = config.authHeader;
|
|
45294
|
+
this._fetch = config.fetchImpl ?? globalThis.fetch;
|
|
45295
|
+
}
|
|
45296
|
+
/** Number of spans waiting to be flushed. */
|
|
45297
|
+
get pendingCount() {
|
|
45298
|
+
return this._buffer.length;
|
|
45299
|
+
}
|
|
45300
|
+
// -- Report methods ---------------------------------------------------------
|
|
45301
|
+
/** Record an LLM inference call. */
|
|
45302
|
+
reportLlmCall(opts) {
|
|
45303
|
+
this._buffer.push(buildLlmSpan(opts));
|
|
45304
|
+
}
|
|
45305
|
+
/** Record a tool/function invocation. */
|
|
45306
|
+
reportToolCall(opts) {
|
|
45307
|
+
this._buffer.push(buildToolSpan(opts));
|
|
45308
|
+
}
|
|
45309
|
+
/** Record an error event. */
|
|
45310
|
+
reportError(opts) {
|
|
45311
|
+
this._buffer.push(buildErrorSpan(opts));
|
|
45312
|
+
}
|
|
45313
|
+
/** Record an arbitrary pre-built span. */
|
|
45314
|
+
reportCustomSpan(span) {
|
|
45315
|
+
this._buffer.push(span);
|
|
45316
|
+
}
|
|
45317
|
+
// -- Flush ------------------------------------------------------------------
|
|
45318
|
+
/**
|
|
45319
|
+
* POST all buffered spans to the backend ingest endpoint.
|
|
45320
|
+
*
|
|
45321
|
+
* - On success (HTTP 2xx): clears the buffer.
|
|
45322
|
+
* - On failure: keeps spans in the buffer for retry.
|
|
45323
|
+
* - Never throws — telemetry is best-effort.
|
|
45324
|
+
*/
|
|
45325
|
+
async flush() {
|
|
45326
|
+
if (this._buffer.length === 0) {
|
|
45327
|
+
return;
|
|
45328
|
+
}
|
|
45329
|
+
const spans = this._buffer;
|
|
45330
|
+
this._buffer = [];
|
|
45331
|
+
try {
|
|
45332
|
+
const response = await this._fetch(`${this._apiBase}/api/v1/telemetry/ingest`, {
|
|
45333
|
+
method: "POST",
|
|
45334
|
+
headers: {
|
|
45335
|
+
"Content-Type": "application/json",
|
|
45336
|
+
Authorization: this._authHeader
|
|
45337
|
+
},
|
|
45338
|
+
body: JSON.stringify({
|
|
45339
|
+
hub_id: this._hubId,
|
|
45340
|
+
spans: spans.map(spanToOtlp)
|
|
45341
|
+
})
|
|
45342
|
+
});
|
|
45343
|
+
if (!response.ok) {
|
|
45344
|
+
this._buffer = spans.concat(this._buffer);
|
|
45345
|
+
}
|
|
45346
|
+
} catch {
|
|
45347
|
+
this._buffer = spans.concat(this._buffer);
|
|
45348
|
+
}
|
|
45349
|
+
}
|
|
45350
|
+
// -- Auto-flush -------------------------------------------------------------
|
|
45351
|
+
/**
|
|
45352
|
+
* Start a periodic flush timer.
|
|
45353
|
+
* @param intervalMs Flush interval in milliseconds (default 30 000).
|
|
45354
|
+
*/
|
|
45355
|
+
startAutoFlush(intervalMs = 3e4) {
|
|
45356
|
+
this.stopAutoFlush();
|
|
45357
|
+
this._timer = setInterval(() => {
|
|
45358
|
+
void this.flush();
|
|
45359
|
+
}, intervalMs);
|
|
45360
|
+
}
|
|
45361
|
+
/** Stop the periodic flush timer. Safe to call when not started. */
|
|
45362
|
+
stopAutoFlush() {
|
|
45363
|
+
if (this._timer !== null) {
|
|
45364
|
+
clearInterval(this._timer);
|
|
45365
|
+
this._timer = null;
|
|
45366
|
+
}
|
|
45367
|
+
}
|
|
45368
|
+
};
|
|
45369
|
+
|
|
45152
45370
|
// src/state.ts
|
|
45153
45371
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
45154
45372
|
import { join } from "node:path";
|
|
@@ -45292,12 +45510,13 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45292
45510
|
_lastWakeTick = Date.now();
|
|
45293
45511
|
_pendingPollTimer = null;
|
|
45294
45512
|
_syncMessageIds = null;
|
|
45295
|
-
/** Recently handled message IDs via WS — survives reconnects so sync skips them. Max 500. */
|
|
45296
|
-
_recentlyHandledIds = /* @__PURE__ */ new Set();
|
|
45297
45513
|
/** Queued A2A messages for responder channels not yet activated (no first initiator message received). */
|
|
45298
45514
|
_a2aPendingQueue = {};
|
|
45299
45515
|
_scanEngine = null;
|
|
45300
45516
|
_scanRuleSetVersion = 0;
|
|
45517
|
+
_telemetryReporter = null;
|
|
45518
|
+
/** Topic ID from the most recent inbound message — used as fallback for replies. */
|
|
45519
|
+
_lastIncomingTopicId;
|
|
45301
45520
|
// Liveness detection: server sends app-level {"event":"ping"} every 30s.
|
|
45302
45521
|
// We check every 30s; if no data received in 90s (3 missed pings), connection is dead.
|
|
45303
45522
|
static PING_INTERVAL_MS = 3e4;
|
|
@@ -45326,6 +45545,10 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45326
45545
|
get sessionCount() {
|
|
45327
45546
|
return this._sessions.size;
|
|
45328
45547
|
}
|
|
45548
|
+
/** Returns the TelemetryReporter instance (available after WebSocket connect). */
|
|
45549
|
+
get telemetry() {
|
|
45550
|
+
return this._telemetryReporter;
|
|
45551
|
+
}
|
|
45329
45552
|
async start() {
|
|
45330
45553
|
this._stopped = false;
|
|
45331
45554
|
await libsodium_wrappers_default.ready;
|
|
@@ -45408,7 +45631,7 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45408
45631
|
if (this._sessions.size === 0) {
|
|
45409
45632
|
throw new Error("No active sessions");
|
|
45410
45633
|
}
|
|
45411
|
-
const topicId = options?.topicId ?? this._persisted?.defaultTopicId;
|
|
45634
|
+
const topicId = options?.topicId ?? this._lastIncomingTopicId ?? this._persisted?.defaultTopicId;
|
|
45412
45635
|
const messageType = options?.messageType ?? "text";
|
|
45413
45636
|
const priority = options?.priority ?? "normal";
|
|
45414
45637
|
const parentSpanId = options?.parentSpanId;
|
|
@@ -45870,6 +46093,11 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45870
46093
|
clearTimeout(this._reconnectTimer);
|
|
45871
46094
|
this._reconnectTimer = null;
|
|
45872
46095
|
}
|
|
46096
|
+
if (this._telemetryReporter) {
|
|
46097
|
+
this._telemetryReporter.stopAutoFlush();
|
|
46098
|
+
await this._telemetryReporter.flush();
|
|
46099
|
+
this._telemetryReporter = null;
|
|
46100
|
+
}
|
|
45873
46101
|
if (this._ws) {
|
|
45874
46102
|
this._ws.removeAllListeners();
|
|
45875
46103
|
this._ws.close();
|
|
@@ -46393,26 +46621,20 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46393
46621
|
this._scanEngine = new ScanEngine();
|
|
46394
46622
|
await this._fetchScanRules();
|
|
46395
46623
|
}
|
|
46624
|
+
if (!this._telemetryReporter && this._persisted?.deviceJwt && this._persisted?.hubAddress) {
|
|
46625
|
+
this._telemetryReporter = new TelemetryReporter({
|
|
46626
|
+
apiBase: this.config.apiUrl,
|
|
46627
|
+
hubId: this._persisted.hubAddress,
|
|
46628
|
+
authHeader: `Bearer ${this._persisted.deviceJwt}`
|
|
46629
|
+
});
|
|
46630
|
+
this._telemetryReporter.startAutoFlush(3e4);
|
|
46631
|
+
}
|
|
46396
46632
|
this.emit("ready");
|
|
46397
46633
|
} catch (openErr) {
|
|
46398
46634
|
console.error("[SecureChannel] Error in WS open handler:", openErr);
|
|
46399
46635
|
this.emit("error", openErr);
|
|
46400
46636
|
}
|
|
46401
46637
|
});
|
|
46402
|
-
const _onUnhandledRejection = (reason) => {
|
|
46403
|
-
console.error("[SecureChannel] UNHANDLED REJECTION (would crash process):", reason);
|
|
46404
|
-
};
|
|
46405
|
-
const _onUncaughtException = (err) => {
|
|
46406
|
-
console.error("[SecureChannel] UNCAUGHT EXCEPTION (would crash process):", err);
|
|
46407
|
-
};
|
|
46408
|
-
process.on("unhandledRejection", _onUnhandledRejection);
|
|
46409
|
-
process.on("uncaughtException", _onUncaughtException);
|
|
46410
|
-
ws.on("close", (code, reason) => {
|
|
46411
|
-
const reasonStr = reason?.toString() || "";
|
|
46412
|
-
console.log(`[SecureChannel] WS CLOSED: code=${code} reason=${JSON.stringify(reasonStr)}`);
|
|
46413
|
-
process.removeListener("unhandledRejection", _onUnhandledRejection);
|
|
46414
|
-
process.removeListener("uncaughtException", _onUncaughtException);
|
|
46415
|
-
});
|
|
46416
46638
|
ws.on("message", async (raw) => {
|
|
46417
46639
|
this._lastServerMessage = Date.now();
|
|
46418
46640
|
this._lastWakeTick = Date.now();
|
|
@@ -46432,10 +46654,8 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46432
46654
|
return;
|
|
46433
46655
|
}
|
|
46434
46656
|
if (data.event === "message") {
|
|
46435
|
-
console.log(`[SecureChannel] \u2190 Direct message received: msg=${data.data?.message_id?.slice(0, 8) ?? "?"} conv=${data.data?.conversation_id?.slice(0, 8) ?? "?"}`);
|
|
46436
46657
|
try {
|
|
46437
46658
|
await this._handleIncomingMessage(data.data);
|
|
46438
|
-
console.log(`[SecureChannel] \u2190 Direct message processed OK: msg=${data.data?.message_id?.slice(0, 8) ?? "?"}`);
|
|
46439
46659
|
} catch (msgErr) {
|
|
46440
46660
|
console.error(
|
|
46441
46661
|
`[SecureChannel] Message handler failed for conv ${data.data?.conversation_id?.slice(0, 8) ?? "?"}...:`,
|
|
@@ -46767,9 +46987,7 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46767
46987
|
this.emit("error", err);
|
|
46768
46988
|
}
|
|
46769
46989
|
});
|
|
46770
|
-
ws.on("close", (
|
|
46771
|
-
const reasonStr = reason?.toString() || "";
|
|
46772
|
-
console.log(`[SecureChannel] WS close handler: code=${code} reason=${JSON.stringify(reasonStr)}`);
|
|
46990
|
+
ws.on("close", () => {
|
|
46773
46991
|
this._stopPing();
|
|
46774
46992
|
this._stopWakeDetector();
|
|
46775
46993
|
this._stopPendingPoll();
|
|
@@ -46791,11 +47009,6 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46791
47009
|
if (this._syncMessageIds?.has(msgData.message_id)) {
|
|
46792
47010
|
return;
|
|
46793
47011
|
}
|
|
46794
|
-
this._recentlyHandledIds.add(msgData.message_id);
|
|
46795
|
-
if (this._recentlyHandledIds.size > 500) {
|
|
46796
|
-
const all = [...this._recentlyHandledIds];
|
|
46797
|
-
this._recentlyHandledIds = new Set(all.slice(all.length - 400));
|
|
46798
|
-
}
|
|
46799
47012
|
const convId = msgData.conversation_id;
|
|
46800
47013
|
const session = this._sessions.get(convId);
|
|
46801
47014
|
if (!session) {
|
|
@@ -46808,14 +47021,7 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46808
47021
|
header_blob: msgData.header_blob,
|
|
46809
47022
|
ciphertext: msgData.ciphertext
|
|
46810
47023
|
});
|
|
46811
|
-
|
|
46812
|
-
try {
|
|
46813
|
-
plaintext = session.ratchet.decrypt(encrypted);
|
|
46814
|
-
} catch (decryptErr) {
|
|
46815
|
-
console.error(`[SecureChannel] Direct message decrypt FAILED for conv ${convId.slice(0, 8)}...: ${String(decryptErr)}`);
|
|
46816
|
-
throw decryptErr;
|
|
46817
|
-
}
|
|
46818
|
-
console.log(`[SecureChannel] Direct message decrypted OK for conv ${convId.slice(0, 8)}...`);
|
|
47024
|
+
const plaintext = session.ratchet.decrypt(encrypted);
|
|
46819
47025
|
this._sendAck(msgData.message_id);
|
|
46820
47026
|
if (!session.activated) {
|
|
46821
47027
|
session.activated = true;
|
|
@@ -46850,6 +47056,7 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46850
47056
|
}
|
|
46851
47057
|
if (messageType === "message") {
|
|
46852
47058
|
const topicId = msgData.topic_id;
|
|
47059
|
+
this._lastIncomingTopicId = topicId;
|
|
46853
47060
|
let attachData;
|
|
46854
47061
|
if (attachmentInfo) {
|
|
46855
47062
|
try {
|
|
@@ -46908,13 +47115,7 @@ ${messageText}`;
|
|
|
46908
47115
|
Promise.resolve(this.config.onMessage?.(emitText, metadata)).catch((err) => {
|
|
46909
47116
|
console.error("[SecureChannel] onMessage callback error:", err);
|
|
46910
47117
|
});
|
|
46911
|
-
|
|
46912
|
-
try {
|
|
46913
|
-
await this._relaySyncToSiblings(convId, session.ownerDeviceId, messageText, topicId);
|
|
46914
|
-
console.log(`[SecureChannel] Sync relay complete for conv ${convId.slice(0, 8)}...`);
|
|
46915
|
-
} catch (relayErr) {
|
|
46916
|
-
console.error(`[SecureChannel] Sync relay FAILED: ${String(relayErr)}`);
|
|
46917
|
-
}
|
|
47118
|
+
await this._relaySyncToSiblings(convId, session.ownerDeviceId, messageText, topicId);
|
|
46918
47119
|
}
|
|
46919
47120
|
if (this._persisted) {
|
|
46920
47121
|
this._persisted.lastMessageTimestamp = msgData.created_at;
|
|
@@ -47039,29 +47240,22 @@ ${messageText}`;
|
|
|
47039
47240
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47040
47241
|
topicId
|
|
47041
47242
|
});
|
|
47042
|
-
let relayed = 0;
|
|
47043
47243
|
for (const [siblingConvId, siblingSession] of this._sessions) {
|
|
47044
47244
|
if (siblingConvId === sourceConvId) continue;
|
|
47045
47245
|
if (!siblingSession.activated) continue;
|
|
47046
|
-
|
|
47047
|
-
|
|
47048
|
-
|
|
47049
|
-
|
|
47050
|
-
|
|
47051
|
-
|
|
47052
|
-
|
|
47053
|
-
|
|
47054
|
-
|
|
47055
|
-
|
|
47056
|
-
|
|
47057
|
-
|
|
47058
|
-
);
|
|
47059
|
-
relayed++;
|
|
47060
|
-
} catch (err) {
|
|
47061
|
-
console.error(`[SecureChannel] Sync send failed for sibling ${siblingConvId.slice(0, 8)}...: ${String(err)}`);
|
|
47062
|
-
}
|
|
47246
|
+
const syncEncrypted = siblingSession.ratchet.encrypt(syncPayload);
|
|
47247
|
+
const syncTransport = encryptedMessageToTransport(syncEncrypted);
|
|
47248
|
+
this._ws.send(
|
|
47249
|
+
JSON.stringify({
|
|
47250
|
+
event: "message",
|
|
47251
|
+
data: {
|
|
47252
|
+
conversation_id: siblingConvId,
|
|
47253
|
+
header_blob: syncTransport.header_blob,
|
|
47254
|
+
ciphertext: syncTransport.ciphertext
|
|
47255
|
+
}
|
|
47256
|
+
})
|
|
47257
|
+
);
|
|
47063
47258
|
}
|
|
47064
|
-
console.log(`[SecureChannel] _relaySyncToSiblings: relayed to ${relayed}/${this._sessions.size - 1} siblings`);
|
|
47065
47259
|
}
|
|
47066
47260
|
/**
|
|
47067
47261
|
* Send stored message history to a newly-activated session.
|
|
@@ -47163,12 +47357,52 @@ ${messageText}`;
|
|
|
47163
47357
|
);
|
|
47164
47358
|
return;
|
|
47165
47359
|
}
|
|
47166
|
-
|
|
47360
|
+
let session = this._sessions.get(convId);
|
|
47167
47361
|
if (!session) {
|
|
47168
47362
|
console.warn(
|
|
47169
|
-
`[SecureChannel] No session for room conv ${convId.slice(0, 8)}...,
|
|
47363
|
+
`[SecureChannel] No session for room conv ${convId.slice(0, 8)}..., fetching room data`
|
|
47170
47364
|
);
|
|
47171
|
-
|
|
47365
|
+
try {
|
|
47366
|
+
const roomRes = await fetch(
|
|
47367
|
+
`${this.config.apiUrl}/api/v1/rooms/${msgData.room_id}`,
|
|
47368
|
+
{
|
|
47369
|
+
headers: {
|
|
47370
|
+
Authorization: `Bearer ${this._persisted.deviceJwt}`
|
|
47371
|
+
}
|
|
47372
|
+
}
|
|
47373
|
+
);
|
|
47374
|
+
if (roomRes.ok) {
|
|
47375
|
+
const roomData = await roomRes.json();
|
|
47376
|
+
await this.joinRoom({
|
|
47377
|
+
roomId: roomData.id,
|
|
47378
|
+
name: roomData.name,
|
|
47379
|
+
members: (roomData.members || []).map((m2) => ({
|
|
47380
|
+
deviceId: m2.device_id,
|
|
47381
|
+
entityType: m2.entity_type,
|
|
47382
|
+
displayName: m2.display_name,
|
|
47383
|
+
identityPublicKey: m2.identity_public_key,
|
|
47384
|
+
ephemeralPublicKey: m2.ephemeral_public_key
|
|
47385
|
+
})),
|
|
47386
|
+
conversations: (roomData.conversations || []).map((c2) => ({
|
|
47387
|
+
id: c2.id,
|
|
47388
|
+
participantA: c2.participant_a,
|
|
47389
|
+
participantB: c2.participant_b
|
|
47390
|
+
}))
|
|
47391
|
+
});
|
|
47392
|
+
session = this._sessions.get(convId);
|
|
47393
|
+
}
|
|
47394
|
+
} catch (fetchErr) {
|
|
47395
|
+
console.error(
|
|
47396
|
+
`[SecureChannel] Failed to fetch room data for ${msgData.room_id}:`,
|
|
47397
|
+
fetchErr
|
|
47398
|
+
);
|
|
47399
|
+
}
|
|
47400
|
+
if (!session) {
|
|
47401
|
+
console.warn(
|
|
47402
|
+
`[SecureChannel] Still no session for room conv ${convId.slice(0, 8)}... after refresh, skipping`
|
|
47403
|
+
);
|
|
47404
|
+
return;
|
|
47405
|
+
}
|
|
47172
47406
|
}
|
|
47173
47407
|
const encrypted = transportToEncryptedMessage({
|
|
47174
47408
|
header_blob: msgData.header_blob,
|
|
@@ -47298,7 +47532,6 @@ ${messageText}`;
|
|
|
47298
47532
|
const PAGE_SIZE = 200;
|
|
47299
47533
|
let since = this._persisted.lastMessageTimestamp;
|
|
47300
47534
|
let totalProcessed = 0;
|
|
47301
|
-
let totalSkipped = 0;
|
|
47302
47535
|
try {
|
|
47303
47536
|
for (let page = 0; page < MAX_PAGES; page++) {
|
|
47304
47537
|
const url = `${this.config.apiUrl}/api/v1/devices/${this._deviceId}/messages?since=${encodeURIComponent(since)}&limit=${PAGE_SIZE}`;
|
|
@@ -47308,24 +47541,15 @@ ${messageText}`;
|
|
|
47308
47541
|
if (!res.ok) break;
|
|
47309
47542
|
const messages = await res.json();
|
|
47310
47543
|
if (messages.length === 0) break;
|
|
47311
|
-
console.log(`[SecureChannel] Sync page ${page}: ${messages.length} messages since ${since}`);
|
|
47312
47544
|
for (const msg of messages) {
|
|
47313
47545
|
if (msg.sender_device_id === this._deviceId) continue;
|
|
47314
47546
|
if (this._syncMessageIds.has(msg.id)) continue;
|
|
47315
47547
|
this._syncMessageIds.add(msg.id);
|
|
47316
|
-
if (this._recentlyHandledIds.has(msg.id)) {
|
|
47317
|
-
this._persisted.lastMessageTimestamp = msg.created_at;
|
|
47318
|
-
since = msg.created_at;
|
|
47319
|
-
totalSkipped++;
|
|
47320
|
-
continue;
|
|
47321
|
-
}
|
|
47322
47548
|
const session = this._sessions.get(msg.conversation_id);
|
|
47323
47549
|
if (!session) {
|
|
47324
47550
|
console.warn(
|
|
47325
47551
|
`[SecureChannel] No session for conversation ${msg.conversation_id} during sync, skipping`
|
|
47326
47552
|
);
|
|
47327
|
-
this._persisted.lastMessageTimestamp = msg.created_at;
|
|
47328
|
-
since = msg.created_at;
|
|
47329
47553
|
continue;
|
|
47330
47554
|
}
|
|
47331
47555
|
try {
|
|
@@ -47359,6 +47583,9 @@ ${messageText}`;
|
|
|
47359
47583
|
topicId
|
|
47360
47584
|
};
|
|
47361
47585
|
this.emit("message", messageText, metadata);
|
|
47586
|
+
Promise.resolve(this.config.onMessage?.(messageText, metadata)).catch((err) => {
|
|
47587
|
+
console.error("[SecureChannel] onMessage callback error:", err);
|
|
47588
|
+
});
|
|
47362
47589
|
}
|
|
47363
47590
|
this._persisted.lastMessageTimestamp = msg.created_at;
|
|
47364
47591
|
since = msg.created_at;
|
|
@@ -47375,15 +47602,10 @@ ${messageText}`;
|
|
|
47375
47602
|
await this._persistState();
|
|
47376
47603
|
if (messages.length < PAGE_SIZE) break;
|
|
47377
47604
|
}
|
|
47378
|
-
if (totalProcessed > 0
|
|
47379
|
-
console.log(`[SecureChannel]
|
|
47380
|
-
}
|
|
47381
|
-
} catch (outerErr) {
|
|
47382
|
-
console.warn(`[SecureChannel] Sync interrupted: ${String(outerErr)}`);
|
|
47383
|
-
try {
|
|
47384
|
-
await this._persistState();
|
|
47385
|
-
} catch {
|
|
47605
|
+
if (totalProcessed > 0) {
|
|
47606
|
+
console.log(`[SecureChannel] Synced ${totalProcessed} missed messages`);
|
|
47386
47607
|
}
|
|
47608
|
+
} catch {
|
|
47387
47609
|
}
|
|
47388
47610
|
this._syncMessageIds = null;
|
|
47389
47611
|
}
|