@agentvault/secure-channel 0.4.1 → 0.4.3
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/channel.d.ts +21 -1
- package/dist/channel.d.ts.map +1 -1
- package/dist/cli.js +109 -14
- package/dist/cli.js.map +3 -3
- package/dist/index.js +109 -14
- package/dist/index.js.map +3 -3
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/channel.d.ts
CHANGED
|
@@ -33,8 +33,28 @@ export declare class SecureChannel extends EventEmitter {
|
|
|
33
33
|
* Encrypt and send a message to ALL owner devices (fanout).
|
|
34
34
|
* Each session gets the same plaintext encrypted independently.
|
|
35
35
|
*/
|
|
36
|
-
send(plaintext: string
|
|
36
|
+
send(plaintext: string, options?: {
|
|
37
|
+
topicId?: string;
|
|
38
|
+
}): Promise<void>;
|
|
37
39
|
stop(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Create a new topic within the conversation group.
|
|
42
|
+
* Requires the channel to be initialized with a groupId (from activation).
|
|
43
|
+
*/
|
|
44
|
+
createTopic(name: string): Promise<{
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
isDefault: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* List all topics in the conversation group.
|
|
51
|
+
* Requires the channel to be initialized with a groupId (from activation).
|
|
52
|
+
*/
|
|
53
|
+
listTopics(): Promise<Array<{
|
|
54
|
+
id: string;
|
|
55
|
+
name: string;
|
|
56
|
+
isDefault: boolean;
|
|
57
|
+
}>>;
|
|
38
58
|
private _enroll;
|
|
39
59
|
private _poll;
|
|
40
60
|
private _activate;
|
package/dist/channel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EAKb,MAAM,YAAY,CAAC;AAiDpB,qBAAa,aAAc,SAAQ,YAAY;IAiBjC,OAAO,CAAC,MAAM;IAhB1B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,sBAAsB,CAAc;IAC5C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,SAAS,CAGH;IACd,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAA+B;gBAE7B,MAAM,EAAE,mBAAmB;IAI/C,IAAI,KAAK,IAAI,YAAY,CAExB;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,iEAAiE;IACjE,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,2CAA2C;IAC3C,IAAI,eAAe,IAAI,MAAM,EAAE,CAE9B;IAED,6CAA6C;IAC7C,IAAI,YAAY,IAAI,MAAM,CAEzB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC5B;;OAEG;IACH,OAAO,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EAKb,MAAM,YAAY,CAAC;AAiDpB,qBAAa,aAAc,SAAQ,YAAY;IAiBjC,OAAO,CAAC,MAAM;IAhB1B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,sBAAsB,CAAc;IAC5C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,SAAS,CAGH;IACd,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAA+B;gBAE7B,MAAM,EAAE,mBAAmB;IAI/C,IAAI,KAAK,IAAI,YAAY,CAExB;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,iEAAiE;IACjE,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,2CAA2C;IAC3C,IAAI,eAAe,IAAI,MAAM,EAAE,CAE9B;IAED,6CAA6C;IAC7C,IAAI,YAAY,IAAI,MAAM,CAEzB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;OAGG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAsC1F;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;YAiCtE,OAAO;IAgDrB,OAAO,CAAC,KAAK;YAgCC,SAAS;IAyIvB,OAAO,CAAC,QAAQ;IAsEhB;;;;OAIG;YACW,sBAAsB;IAwFpC;;;OAGG;YACW,oBAAoB;IAqClC;;;OAGG;YACW,uBAAuB;IAkCrC;;;;OAIG;YACW,mBAAmB;IAkEjC;;;OAGG;YACW,mBAAmB;IA4FjC,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,YAAY;IAKpB;;;OAGG;YACW,aAAa;CAc5B"}
|
package/dist/cli.js
CHANGED
|
@@ -45033,7 +45033,7 @@ async function activateDevice(apiUrl2, deviceId) {
|
|
|
45033
45033
|
}
|
|
45034
45034
|
|
|
45035
45035
|
// src/channel.ts
|
|
45036
|
-
var POLL_INTERVAL_MS =
|
|
45036
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
45037
45037
|
var RECONNECT_BASE_MS = 1e3;
|
|
45038
45038
|
var RECONNECT_MAX_MS = 3e4;
|
|
45039
45039
|
function migratePersistedState(raw) {
|
|
@@ -45129,17 +45129,21 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45129
45129
|
/**
|
|
45130
45130
|
* Append a message to persistent history for cross-device replay.
|
|
45131
45131
|
*/
|
|
45132
|
-
_appendHistory(sender, text) {
|
|
45132
|
+
_appendHistory(sender, text, topicId) {
|
|
45133
45133
|
if (!this._persisted) return;
|
|
45134
45134
|
if (!this._persisted.messageHistory) {
|
|
45135
45135
|
this._persisted.messageHistory = [];
|
|
45136
45136
|
}
|
|
45137
45137
|
const maxSize = this.config.maxHistorySize ?? 500;
|
|
45138
|
-
|
|
45138
|
+
const entry = {
|
|
45139
45139
|
sender,
|
|
45140
45140
|
text,
|
|
45141
45141
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
45142
|
-
}
|
|
45142
|
+
};
|
|
45143
|
+
if (topicId) {
|
|
45144
|
+
entry.topicId = topicId;
|
|
45145
|
+
}
|
|
45146
|
+
this._persisted.messageHistory.push(entry);
|
|
45143
45147
|
if (this._persisted.messageHistory.length > maxSize) {
|
|
45144
45148
|
this._persisted.messageHistory = this._persisted.messageHistory.slice(-maxSize);
|
|
45145
45149
|
}
|
|
@@ -45148,11 +45152,12 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45148
45152
|
* Encrypt and send a message to ALL owner devices (fanout).
|
|
45149
45153
|
* Each session gets the same plaintext encrypted independently.
|
|
45150
45154
|
*/
|
|
45151
|
-
async send(plaintext) {
|
|
45155
|
+
async send(plaintext, options) {
|
|
45152
45156
|
if (this._state !== "ready" || this._sessions.size === 0 || !this._ws) {
|
|
45153
45157
|
throw new Error("Channel is not ready");
|
|
45154
45158
|
}
|
|
45155
|
-
this.
|
|
45159
|
+
const topicId = options?.topicId ?? this._persisted?.defaultTopicId;
|
|
45160
|
+
this._appendHistory("agent", plaintext, topicId);
|
|
45156
45161
|
const messageGroupId = randomUUID();
|
|
45157
45162
|
for (const [convId, session] of this._sessions) {
|
|
45158
45163
|
if (!session.activated) {
|
|
@@ -45167,7 +45172,8 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45167
45172
|
conversation_id: convId,
|
|
45168
45173
|
header_blob: transport.header_blob,
|
|
45169
45174
|
ciphertext: transport.ciphertext,
|
|
45170
|
-
message_group_id: messageGroupId
|
|
45175
|
+
message_group_id: messageGroupId,
|
|
45176
|
+
topic_id: topicId
|
|
45171
45177
|
}
|
|
45172
45178
|
})
|
|
45173
45179
|
);
|
|
@@ -45191,6 +45197,72 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45191
45197
|
}
|
|
45192
45198
|
this._setState("disconnected");
|
|
45193
45199
|
}
|
|
45200
|
+
// --- Topic management ---
|
|
45201
|
+
/**
|
|
45202
|
+
* Create a new topic within the conversation group.
|
|
45203
|
+
* Requires the channel to be initialized with a groupId (from activation).
|
|
45204
|
+
*/
|
|
45205
|
+
async createTopic(name2) {
|
|
45206
|
+
if (!this._persisted?.groupId) {
|
|
45207
|
+
throw new Error("Channel not initialized or groupId unknown");
|
|
45208
|
+
}
|
|
45209
|
+
if (!this._deviceJwt) {
|
|
45210
|
+
throw new Error("Channel not authenticated");
|
|
45211
|
+
}
|
|
45212
|
+
const res = await fetch(`${this.config.apiUrl}/api/v1/topics`, {
|
|
45213
|
+
method: "POST",
|
|
45214
|
+
headers: {
|
|
45215
|
+
"Content-Type": "application/json",
|
|
45216
|
+
Authorization: `Bearer ${this._deviceJwt}`
|
|
45217
|
+
},
|
|
45218
|
+
body: JSON.stringify({
|
|
45219
|
+
group_id: this._persisted.groupId,
|
|
45220
|
+
name: name2,
|
|
45221
|
+
creator_device_id: this._persisted.deviceId
|
|
45222
|
+
})
|
|
45223
|
+
});
|
|
45224
|
+
if (!res.ok) {
|
|
45225
|
+
const detail = await res.text();
|
|
45226
|
+
throw new Error(`Create topic failed (${res.status}): ${detail}`);
|
|
45227
|
+
}
|
|
45228
|
+
const resp = await res.json();
|
|
45229
|
+
const topic = { id: resp.id, name: resp.name, isDefault: resp.is_default };
|
|
45230
|
+
if (!this._persisted.topics) {
|
|
45231
|
+
this._persisted.topics = [];
|
|
45232
|
+
}
|
|
45233
|
+
this._persisted.topics.push(topic);
|
|
45234
|
+
await this._persistState();
|
|
45235
|
+
return topic;
|
|
45236
|
+
}
|
|
45237
|
+
/**
|
|
45238
|
+
* List all topics in the conversation group.
|
|
45239
|
+
* Requires the channel to be initialized with a groupId (from activation).
|
|
45240
|
+
*/
|
|
45241
|
+
async listTopics() {
|
|
45242
|
+
if (!this._persisted?.groupId) {
|
|
45243
|
+
throw new Error("Channel not initialized or groupId unknown");
|
|
45244
|
+
}
|
|
45245
|
+
if (!this._deviceJwt) {
|
|
45246
|
+
throw new Error("Channel not authenticated");
|
|
45247
|
+
}
|
|
45248
|
+
const res = await fetch(
|
|
45249
|
+
`${this.config.apiUrl}/api/v1/topics?group_id=${encodeURIComponent(this._persisted.groupId)}`,
|
|
45250
|
+
{
|
|
45251
|
+
headers: {
|
|
45252
|
+
Authorization: `Bearer ${this._deviceJwt}`
|
|
45253
|
+
}
|
|
45254
|
+
}
|
|
45255
|
+
);
|
|
45256
|
+
if (!res.ok) {
|
|
45257
|
+
const detail = await res.text();
|
|
45258
|
+
throw new Error(`List topics failed (${res.status}): ${detail}`);
|
|
45259
|
+
}
|
|
45260
|
+
const resp = await res.json();
|
|
45261
|
+
const topics = resp.map((t2) => ({ id: t2.id, name: t2.name, isDefault: t2.is_default }));
|
|
45262
|
+
this._persisted.topics = topics;
|
|
45263
|
+
await this._persistState();
|
|
45264
|
+
return topics;
|
|
45265
|
+
}
|
|
45194
45266
|
// --- Internal lifecycle ---
|
|
45195
45267
|
async _enroll() {
|
|
45196
45268
|
this._setState("enrolling");
|
|
@@ -45317,6 +45389,15 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45317
45389
|
sessions,
|
|
45318
45390
|
messageHistory: this._persisted.messageHistory ?? []
|
|
45319
45391
|
};
|
|
45392
|
+
if (conversations.length > 0) {
|
|
45393
|
+
const firstConv = conversations[0];
|
|
45394
|
+
if (firstConv.group_id) {
|
|
45395
|
+
this._persisted.groupId = firstConv.group_id;
|
|
45396
|
+
}
|
|
45397
|
+
if (firstConv.default_topic_id) {
|
|
45398
|
+
this._persisted.defaultTopicId = firstConv.default_topic_id;
|
|
45399
|
+
}
|
|
45400
|
+
}
|
|
45320
45401
|
await saveState(this.config.dataDir, this._persisted);
|
|
45321
45402
|
if (this.config.webhookUrl) {
|
|
45322
45403
|
try {
|
|
@@ -45354,6 +45435,14 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45354
45435
|
}
|
|
45355
45436
|
_connect() {
|
|
45356
45437
|
if (this._stopped) return;
|
|
45438
|
+
if (this._ws) {
|
|
45439
|
+
this._ws.removeAllListeners();
|
|
45440
|
+
try {
|
|
45441
|
+
this._ws.close();
|
|
45442
|
+
} catch {
|
|
45443
|
+
}
|
|
45444
|
+
this._ws = null;
|
|
45445
|
+
}
|
|
45357
45446
|
this._setState("connecting");
|
|
45358
45447
|
const wsUrl = this.config.apiUrl.replace(/^http/, "ws");
|
|
45359
45448
|
const url = `${wsUrl}/api/v1/ws?token=${encodeURIComponent(this._deviceJwt)}&device_id=${this._deviceId}`;
|
|
@@ -45438,15 +45527,17 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45438
45527
|
return;
|
|
45439
45528
|
}
|
|
45440
45529
|
if (messageType === "message") {
|
|
45441
|
-
|
|
45530
|
+
const topicId = msgData.topic_id;
|
|
45531
|
+
this._appendHistory("owner", messageText, topicId);
|
|
45442
45532
|
const metadata = {
|
|
45443
45533
|
messageId: msgData.message_id,
|
|
45444
45534
|
conversationId: convId,
|
|
45445
|
-
timestamp: msgData.created_at
|
|
45535
|
+
timestamp: msgData.created_at,
|
|
45536
|
+
topicId
|
|
45446
45537
|
};
|
|
45447
45538
|
this.emit("message", messageText, metadata);
|
|
45448
45539
|
this.config.onMessage?.(messageText, metadata);
|
|
45449
|
-
await this._relaySyncToSiblings(convId, session.ownerDeviceId, messageText);
|
|
45540
|
+
await this._relaySyncToSiblings(convId, session.ownerDeviceId, messageText, topicId);
|
|
45450
45541
|
}
|
|
45451
45542
|
if (this._persisted) {
|
|
45452
45543
|
this._persisted.lastMessageTimestamp = msgData.created_at;
|
|
@@ -45457,13 +45548,14 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45457
45548
|
* Relay an owner's message to all sibling sessions as encrypted sync messages.
|
|
45458
45549
|
* This allows all owner devices to see messages from any single device.
|
|
45459
45550
|
*/
|
|
45460
|
-
async _relaySyncToSiblings(sourceConvId, senderOwnerDeviceId, messageText) {
|
|
45551
|
+
async _relaySyncToSiblings(sourceConvId, senderOwnerDeviceId, messageText, topicId) {
|
|
45461
45552
|
if (!this._ws || this._sessions.size <= 1) return;
|
|
45462
45553
|
const syncPayload = JSON.stringify({
|
|
45463
45554
|
type: "sync",
|
|
45464
45555
|
sender: senderOwnerDeviceId,
|
|
45465
45556
|
text: messageText,
|
|
45466
|
-
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
45557
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
45558
|
+
topicId
|
|
45467
45559
|
});
|
|
45468
45560
|
for (const [siblingConvId, siblingSession] of this._sessions) {
|
|
45469
45561
|
if (siblingConvId === sourceConvId) continue;
|
|
@@ -45613,11 +45705,13 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45613
45705
|
messageText = plaintext;
|
|
45614
45706
|
}
|
|
45615
45707
|
if (messageType === "message") {
|
|
45616
|
-
|
|
45708
|
+
const topicId = msg.topic_id;
|
|
45709
|
+
this._appendHistory("owner", messageText, topicId);
|
|
45617
45710
|
const metadata = {
|
|
45618
45711
|
messageId: msg.id,
|
|
45619
45712
|
conversationId: msg.conversation_id,
|
|
45620
|
-
timestamp: msg.created_at
|
|
45713
|
+
timestamp: msg.created_at,
|
|
45714
|
+
topicId
|
|
45621
45715
|
};
|
|
45622
45716
|
this.emit("message", messageText, metadata);
|
|
45623
45717
|
this.config.onMessage?.(messageText, metadata);
|
|
@@ -45634,6 +45728,7 @@ var SecureChannel = class extends EventEmitter {
|
|
|
45634
45728
|
}
|
|
45635
45729
|
_scheduleReconnect() {
|
|
45636
45730
|
if (this._stopped) return;
|
|
45731
|
+
if (this._reconnectTimer) return;
|
|
45637
45732
|
const delay = Math.min(
|
|
45638
45733
|
RECONNECT_BASE_MS * Math.pow(2, this._reconnectAttempt),
|
|
45639
45734
|
RECONNECT_MAX_MS
|