@agentvault/agentvault 0.8.0 → 0.9.1
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__/install-plugin.test.d.ts +2 -0
- package/dist/__tests__/install-plugin.test.d.ts.map +1 -0
- package/dist/channel.d.ts.map +1 -1
- package/dist/cli.js +312 -37
- package/dist/cli.js.map +4 -4
- package/dist/crypto-helpers.d.ts +1 -11
- package/dist/crypto-helpers.d.ts.map +1 -1
- package/dist/gateway-send.d.ts +37 -0
- package/dist/gateway-send.d.ts.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +236 -20
- package/dist/index.js.map +4 -4
- package/dist/openclaw-entry.d.ts.map +1 -1
- package/dist/openclaw-entry.js +13 -8
- package/dist/openclaw-entry.js.map +2 -2
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/crypto-helpers.d.ts
CHANGED
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function hexToBytes(hex: string): Uint8Array;
|
|
3
|
-
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
4
|
-
export declare function base64ToBytes(b64: string): Uint8Array;
|
|
5
|
-
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
6
|
-
export interface TransportMessage {
|
|
7
|
-
header_blob: string;
|
|
8
|
-
ciphertext: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function encryptedMessageToTransport(msg: EncryptedMessage): TransportMessage;
|
|
11
|
-
export declare function transportToEncryptedMessage(transport: TransportMessage): EncryptedMessage;
|
|
1
|
+
export { hexToBytes, bytesToHex, base64ToBytes, bytesToBase64, encryptedMessageToTransport, transportToEncryptedMessage, type TransportMessage, } from "@agentvault/crypto";
|
|
12
2
|
//# sourceMappingURL=crypto-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto-helpers.d.ts","sourceRoot":"","sources":["../src/crypto-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypto-helpers.d.ts","sourceRoot":"","sources":["../src/crypto-helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,2BAA2B,EAC3B,2BAA2B,EAC3B,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway send helper — lets agents send messages to their owner
|
|
3
|
+
* via the plugin's local HTTP server (port 18790).
|
|
4
|
+
*
|
|
5
|
+
* Delivery path: sendToOwner() → plugin HTTP /send → SecureChannel.send()
|
|
6
|
+
* → Double Ratchet encrypt → WebSocket → backend → owner's app
|
|
7
|
+
*/
|
|
8
|
+
export interface GatewaySendOptions {
|
|
9
|
+
/** Override the gateway port (default: env GATEWAY_SEND_PORT or 18790) */
|
|
10
|
+
port?: number;
|
|
11
|
+
/** Override the gateway host (default: 127.0.0.1) */
|
|
12
|
+
host?: string;
|
|
13
|
+
/** AbortSignal for cancellation */
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export interface GatewaySendResult {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface GatewayStatusResult {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
state?: string;
|
|
23
|
+
deviceId?: string;
|
|
24
|
+
sessions?: number;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Send a plaintext message to the agent's owner via the gateway HTTP server.
|
|
29
|
+
* Never throws — returns `{ ok: false, error }` on failure.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sendToOwner(text: string, options?: GatewaySendOptions): Promise<GatewaySendResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Check gateway health / status.
|
|
34
|
+
* Never throws — returns `{ ok: false, error }` on failure.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkGateway(options?: GatewaySendOptions): Promise<GatewayStatusResult>;
|
|
37
|
+
//# sourceMappingURL=gateway-send.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-send.d.ts","sourceRoot":"","sources":["../src/gateway-send.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsBD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAuB5B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAmB9B"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { SecureChannel } from "./channel.js";
|
|
2
2
|
export type { SecureChannelConfig, ChannelState, MessageMetadata, AttachmentData, PersistedState, LegacyPersistedState, DeviceSession, HistoryEntry, SendOptions, DecisionOption, DecisionRequest, DecisionResponse, ContextRef, HeartbeatStatus, StatusAlert, RoomInfo, RoomMemberInfo, RoomConversationInfo, RoomState, A2AChannel, A2AMessage, RoomParticipantEvent, } from "./types.js";
|
|
3
3
|
export { agentVaultPlugin, setOcRuntime, getActiveChannel } from "./openclaw-plugin.js";
|
|
4
|
-
export
|
|
4
|
+
export { sendToOwner, checkGateway } from "./gateway-send.js";
|
|
5
|
+
export type { GatewaySendOptions, GatewaySendResult, GatewayStatusResult, } from "./gateway-send.js";
|
|
6
|
+
export declare const VERSION = "0.9.0";
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,EACX,QAAQ,EACR,cAAc,EACd,oBAAoB,EACpB,SAAS,EACT,UAAU,EACV,UAAU,EACV,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,WAAW,EACX,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,EACX,QAAQ,EACR,cAAc,EACd,oBAAoB,EACpB,SAAS,EACT,UAAU,EACV,UAAU,EACV,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -45106,7 +45106,7 @@ var ScanEngine = class {
|
|
|
45106
45106
|
}
|
|
45107
45107
|
};
|
|
45108
45108
|
|
|
45109
|
-
//
|
|
45109
|
+
// ../crypto/dist/transport.js
|
|
45110
45110
|
function hexToBytes(hex) {
|
|
45111
45111
|
return libsodium_wrappers_default.from_hex(hex);
|
|
45112
45112
|
}
|
|
@@ -45128,18 +45128,14 @@ function encryptedMessageToTransport(msg) {
|
|
|
45128
45128
|
nonce: bytesToBase64(msg.nonce)
|
|
45129
45129
|
};
|
|
45130
45130
|
const headerJson = JSON.stringify(headerObj);
|
|
45131
|
-
const headerBlob = bytesToBase64(
|
|
45132
|
-
new TextEncoder().encode(headerJson)
|
|
45133
|
-
);
|
|
45131
|
+
const headerBlob = bytesToBase64(new TextEncoder().encode(headerJson));
|
|
45134
45132
|
return {
|
|
45135
45133
|
header_blob: headerBlob,
|
|
45136
45134
|
ciphertext: bytesToBase64(msg.ciphertext)
|
|
45137
45135
|
};
|
|
45138
45136
|
}
|
|
45139
45137
|
function transportToEncryptedMessage(transport) {
|
|
45140
|
-
const headerJson = new TextDecoder().decode(
|
|
45141
|
-
base64ToBytes(transport.header_blob)
|
|
45142
|
-
);
|
|
45138
|
+
const headerJson = new TextDecoder().decode(base64ToBytes(transport.header_blob));
|
|
45143
45139
|
const headerObj = JSON.parse(headerJson);
|
|
45144
45140
|
return {
|
|
45145
45141
|
header: {
|
|
@@ -45425,9 +45421,18 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45425
45421
|
scanStatus = scanResult.status;
|
|
45426
45422
|
}
|
|
45427
45423
|
this._appendHistory("agent", plaintext, topicId);
|
|
45424
|
+
const roomConvIds = /* @__PURE__ */ new Set();
|
|
45425
|
+
if (this._persisted?.rooms) {
|
|
45426
|
+
for (const room of Object.values(this._persisted.rooms)) {
|
|
45427
|
+
for (const cid of room.conversationIds) {
|
|
45428
|
+
roomConvIds.add(cid);
|
|
45429
|
+
}
|
|
45430
|
+
}
|
|
45431
|
+
}
|
|
45428
45432
|
const messageGroupId = randomUUID();
|
|
45429
45433
|
for (const [convId, session] of this._sessions) {
|
|
45430
45434
|
if (!session.activated) continue;
|
|
45435
|
+
if (roomConvIds.has(convId)) continue;
|
|
45431
45436
|
const encrypted = session.ratchet.encrypt(plaintext);
|
|
45432
45437
|
const transport = encryptedMessageToTransport(encrypted);
|
|
45433
45438
|
const msg = {
|
|
@@ -45573,6 +45578,10 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45573
45578
|
if (conv.participantA !== myDeviceId && conv.participantB !== myDeviceId) {
|
|
45574
45579
|
continue;
|
|
45575
45580
|
}
|
|
45581
|
+
if (this._sessions.has(conv.id)) {
|
|
45582
|
+
conversationIds.push(conv.id);
|
|
45583
|
+
continue;
|
|
45584
|
+
}
|
|
45576
45585
|
const otherDeviceId = conv.participantA === myDeviceId ? conv.participantB : conv.participantA;
|
|
45577
45586
|
const otherMember = roomData.members.find((m2) => m2.deviceId === otherDeviceId);
|
|
45578
45587
|
if (!otherMember?.identityPublicKey) {
|
|
@@ -45582,13 +45591,12 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45582
45591
|
continue;
|
|
45583
45592
|
}
|
|
45584
45593
|
const isInitiator = myDeviceId < otherDeviceId;
|
|
45594
|
+
const theirEphKey = otherMember.ephemeralPublicKey ?? otherMember.identityPublicKey;
|
|
45585
45595
|
const sharedSecret = performX3DH({
|
|
45586
45596
|
myIdentityPrivate: hexToBytes(identity.privateKey),
|
|
45587
45597
|
myEphemeralPrivate: hexToBytes(ephemeral.privateKey),
|
|
45588
45598
|
theirIdentityPublic: hexToBytes(otherMember.identityPublicKey),
|
|
45589
|
-
theirEphemeralPublic: hexToBytes(
|
|
45590
|
-
otherMember.ephemeralPublicKey ?? otherMember.identityPublicKey
|
|
45591
|
-
),
|
|
45599
|
+
theirEphemeralPublic: hexToBytes(theirEphKey),
|
|
45592
45600
|
isInitiator
|
|
45593
45601
|
});
|
|
45594
45602
|
const ratchet = isInitiator ? DoubleRatchet.initSender(sharedSecret, {
|
|
@@ -45660,9 +45668,11 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
45660
45668
|
this._ws.send(
|
|
45661
45669
|
JSON.stringify({
|
|
45662
45670
|
event: "room_message",
|
|
45663
|
-
|
|
45664
|
-
|
|
45665
|
-
|
|
45671
|
+
data: {
|
|
45672
|
+
room_id: roomId,
|
|
45673
|
+
recipients,
|
|
45674
|
+
message_type: messageType
|
|
45675
|
+
}
|
|
45666
45676
|
})
|
|
45667
45677
|
);
|
|
45668
45678
|
} else {
|
|
@@ -46088,6 +46098,23 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46088
46098
|
if (this._persisted.hubAddress) {
|
|
46089
46099
|
payload2.hub_address = this._persisted.hubAddress;
|
|
46090
46100
|
}
|
|
46101
|
+
if (channelEntry.observerSession?.ratchetState) {
|
|
46102
|
+
try {
|
|
46103
|
+
const obsRatchet = DoubleRatchet.deserialize(channelEntry.observerSession.ratchetState);
|
|
46104
|
+
const obsEncrypted = obsRatchet.encrypt(text);
|
|
46105
|
+
const obsHeaderObj = {
|
|
46106
|
+
dhPublicKey: bytesToHex(obsEncrypted.header.dhPublicKey),
|
|
46107
|
+
previousChainLength: obsEncrypted.header.previousChainLength,
|
|
46108
|
+
messageNumber: obsEncrypted.header.messageNumber
|
|
46109
|
+
};
|
|
46110
|
+
payload2.observer_header_blob = Buffer.from(JSON.stringify(obsHeaderObj)).toString("hex");
|
|
46111
|
+
payload2.observer_ciphertext = bytesToHex(obsEncrypted.ciphertext);
|
|
46112
|
+
payload2.observer_nonce = bytesToHex(obsEncrypted.nonce);
|
|
46113
|
+
channelEntry.observerSession.ratchetState = obsRatchet.serialize();
|
|
46114
|
+
} catch (obsErr) {
|
|
46115
|
+
console.error("[SecureChannel] Observer encryption failed (sending without observer copy):", obsErr);
|
|
46116
|
+
}
|
|
46117
|
+
}
|
|
46091
46118
|
channelEntry.session.ratchetState = ratchet.serialize();
|
|
46092
46119
|
await this._persistState();
|
|
46093
46120
|
this._ws.send(
|
|
@@ -46390,7 +46417,7 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46390
46417
|
const d2 = data.data;
|
|
46391
46418
|
this.joinRoom({
|
|
46392
46419
|
roomId: d2.room_id,
|
|
46393
|
-
name: d2.name,
|
|
46420
|
+
name: d2.room_name ?? d2.name ?? "Room",
|
|
46394
46421
|
members: (d2.members || []).map((m2) => ({
|
|
46395
46422
|
deviceId: m2.device_id,
|
|
46396
46423
|
entityType: m2.entity_type,
|
|
@@ -46543,6 +46570,74 @@ var SecureChannel = class _SecureChannel extends EventEmitter {
|
|
|
46543
46570
|
}
|
|
46544
46571
|
this.emit("a2a_channel_activated", actData);
|
|
46545
46572
|
}
|
|
46573
|
+
if (data.event === "a2a_observer_enabled") {
|
|
46574
|
+
const obsData = data.data || data;
|
|
46575
|
+
const obsChannelId = obsData.channel_id;
|
|
46576
|
+
const obsChannelEntry = this._persisted?.a2aChannels?.[obsChannelId];
|
|
46577
|
+
if (obsChannelEntry && this._persisted && this._ws) {
|
|
46578
|
+
try {
|
|
46579
|
+
const obsEphemeral = await generateEphemeralKeypair();
|
|
46580
|
+
const obsEphPubHex = bytesToHex(obsEphemeral.publicKey);
|
|
46581
|
+
const obsEphPrivHex = bytesToHex(obsEphemeral.privateKey);
|
|
46582
|
+
obsChannelEntry.pendingObserverEphemeralPrivateKey = obsEphPrivHex;
|
|
46583
|
+
await this._persistState();
|
|
46584
|
+
this._ws.send(
|
|
46585
|
+
JSON.stringify({
|
|
46586
|
+
event: "a2a_observer_key_submit",
|
|
46587
|
+
data: {
|
|
46588
|
+
channel_id: obsChannelId,
|
|
46589
|
+
ephemeral_key: obsEphPubHex,
|
|
46590
|
+
side: obsChannelEntry.role || "initiator"
|
|
46591
|
+
}
|
|
46592
|
+
})
|
|
46593
|
+
);
|
|
46594
|
+
console.log(
|
|
46595
|
+
`[SecureChannel] Observer key submitted for channel ${obsChannelId.slice(0, 8)}... (side=${obsChannelEntry.role})`
|
|
46596
|
+
);
|
|
46597
|
+
} catch (err) {
|
|
46598
|
+
console.error("[SecureChannel] Observer key submission failed:", err);
|
|
46599
|
+
}
|
|
46600
|
+
}
|
|
46601
|
+
}
|
|
46602
|
+
if (data.event === "a2a_observer_key_accepted") {
|
|
46603
|
+
const obsAccData = data.data || data;
|
|
46604
|
+
const obsAccChannelId = obsAccData.channel_id;
|
|
46605
|
+
const observerIdentityHex = obsAccData.observer_identity_key;
|
|
46606
|
+
const obsAccSide = obsAccData.side;
|
|
46607
|
+
const obsAccEntry = this._persisted?.a2aChannels?.[obsAccChannelId];
|
|
46608
|
+
if (obsAccEntry && obsAccEntry.pendingObserverEphemeralPrivateKey && this._persisted) {
|
|
46609
|
+
try {
|
|
46610
|
+
const myIdentityPrivate = hexToBytes(this._persisted.identityKeypair.privateKey);
|
|
46611
|
+
const myIdentityPublic = hexToBytes(this._persisted.identityKeypair.publicKey);
|
|
46612
|
+
const myObsEphemeralPrivate = hexToBytes(obsAccEntry.pendingObserverEphemeralPrivateKey);
|
|
46613
|
+
const ownerIdentityPublic = hexToBytes(observerIdentityHex);
|
|
46614
|
+
const obsSharedSecret = performX3DH({
|
|
46615
|
+
myIdentityPrivate,
|
|
46616
|
+
myEphemeralPrivate: myObsEphemeralPrivate,
|
|
46617
|
+
theirIdentityPublic: ownerIdentityPublic,
|
|
46618
|
+
theirEphemeralPublic: ownerIdentityPublic,
|
|
46619
|
+
// owner uses identity as ephemeral
|
|
46620
|
+
isInitiator: true
|
|
46621
|
+
});
|
|
46622
|
+
const identityKp = {
|
|
46623
|
+
publicKey: myIdentityPublic,
|
|
46624
|
+
privateKey: myIdentityPrivate,
|
|
46625
|
+
keyType: "ed25519"
|
|
46626
|
+
};
|
|
46627
|
+
const obsRatchet = DoubleRatchet.initSender(obsSharedSecret, identityKp);
|
|
46628
|
+
obsAccEntry.observerSession = {
|
|
46629
|
+
ratchetState: obsRatchet.serialize()
|
|
46630
|
+
};
|
|
46631
|
+
delete obsAccEntry.pendingObserverEphemeralPrivateKey;
|
|
46632
|
+
await this._persistState();
|
|
46633
|
+
console.log(
|
|
46634
|
+
`[SecureChannel] Observer ratchet initialized for channel ${obsAccChannelId.slice(0, 8)}... (side=${obsAccSide})`
|
|
46635
|
+
);
|
|
46636
|
+
} catch (err) {
|
|
46637
|
+
console.error("[SecureChannel] Observer ratchet init failed:", err);
|
|
46638
|
+
}
|
|
46639
|
+
}
|
|
46640
|
+
}
|
|
46546
46641
|
if (data.event === "a2a_channel_rejected") {
|
|
46547
46642
|
this.emit("a2a_channel_rejected", data.data || data);
|
|
46548
46643
|
}
|
|
@@ -47013,7 +47108,73 @@ ${messageText}`;
|
|
|
47013
47108
|
header_blob: msgData.header_blob,
|
|
47014
47109
|
ciphertext: msgData.ciphertext
|
|
47015
47110
|
});
|
|
47016
|
-
|
|
47111
|
+
let plaintext;
|
|
47112
|
+
try {
|
|
47113
|
+
plaintext = session.ratchet.decrypt(encrypted);
|
|
47114
|
+
} catch (decryptErr) {
|
|
47115
|
+
console.warn(
|
|
47116
|
+
`[SecureChannel] Room decrypt failed for conv ${convId.slice(0, 8)}...: ${String(decryptErr)}, re-initializing ratchet`
|
|
47117
|
+
);
|
|
47118
|
+
try {
|
|
47119
|
+
const roomEntry = this._persisted?.rooms ? Object.values(this._persisted.rooms).find(
|
|
47120
|
+
(r2) => r2.conversationIds.includes(convId)
|
|
47121
|
+
) : null;
|
|
47122
|
+
if (!roomEntry) throw new Error("Room not found for conversation");
|
|
47123
|
+
const otherMember = roomEntry.members.find(
|
|
47124
|
+
(m2) => m2.deviceId === msgData.sender_device_id
|
|
47125
|
+
);
|
|
47126
|
+
if (!otherMember?.identityPublicKey) throw new Error("No key for sender");
|
|
47127
|
+
const isInitiator = this._deviceId < msgData.sender_device_id;
|
|
47128
|
+
const identity = this._persisted.identityKeypair;
|
|
47129
|
+
const ephemeral = this._persisted.ephemeralKeypair;
|
|
47130
|
+
const sharedSecret = performX3DH({
|
|
47131
|
+
myIdentityPrivate: hexToBytes(identity.privateKey),
|
|
47132
|
+
myEphemeralPrivate: hexToBytes(ephemeral.privateKey),
|
|
47133
|
+
theirIdentityPublic: hexToBytes(otherMember.identityPublicKey),
|
|
47134
|
+
theirEphemeralPublic: hexToBytes(
|
|
47135
|
+
otherMember.ephemeralPublicKey ?? otherMember.identityPublicKey
|
|
47136
|
+
),
|
|
47137
|
+
isInitiator
|
|
47138
|
+
});
|
|
47139
|
+
const newRatchet = isInitiator ? DoubleRatchet.initSender(sharedSecret, {
|
|
47140
|
+
publicKey: hexToBytes(identity.publicKey),
|
|
47141
|
+
privateKey: hexToBytes(identity.privateKey),
|
|
47142
|
+
keyType: "ed25519"
|
|
47143
|
+
}) : DoubleRatchet.initReceiver(sharedSecret, {
|
|
47144
|
+
publicKey: hexToBytes(identity.publicKey),
|
|
47145
|
+
privateKey: hexToBytes(identity.privateKey),
|
|
47146
|
+
keyType: "ed25519"
|
|
47147
|
+
});
|
|
47148
|
+
session.ratchet = newRatchet;
|
|
47149
|
+
session.activated = false;
|
|
47150
|
+
this._persisted.sessions[convId] = {
|
|
47151
|
+
ownerDeviceId: session.ownerDeviceId,
|
|
47152
|
+
ratchetState: newRatchet.serialize(),
|
|
47153
|
+
activated: false
|
|
47154
|
+
};
|
|
47155
|
+
await this._persistState();
|
|
47156
|
+
console.log(
|
|
47157
|
+
`[SecureChannel] Room ratchet re-initialized for conv ${convId.slice(0, 8)}...`
|
|
47158
|
+
);
|
|
47159
|
+
plaintext = session.ratchet.decrypt(encrypted);
|
|
47160
|
+
} catch (reinitErr) {
|
|
47161
|
+
console.error(
|
|
47162
|
+
`[SecureChannel] Room ratchet re-init failed for conv ${convId.slice(0, 8)}...:`,
|
|
47163
|
+
reinitErr
|
|
47164
|
+
);
|
|
47165
|
+
return;
|
|
47166
|
+
}
|
|
47167
|
+
}
|
|
47168
|
+
let messageText;
|
|
47169
|
+
let messageType;
|
|
47170
|
+
try {
|
|
47171
|
+
const parsed = JSON.parse(plaintext);
|
|
47172
|
+
messageType = parsed.type || "message";
|
|
47173
|
+
messageText = parsed.text || plaintext;
|
|
47174
|
+
} catch {
|
|
47175
|
+
messageType = "message";
|
|
47176
|
+
messageText = plaintext;
|
|
47177
|
+
}
|
|
47017
47178
|
if (!session.activated) {
|
|
47018
47179
|
session.activated = true;
|
|
47019
47180
|
console.log(
|
|
@@ -47028,16 +47189,17 @@ ${messageText}`;
|
|
|
47028
47189
|
messageId: msgData.message_id ?? "",
|
|
47029
47190
|
conversationId: convId,
|
|
47030
47191
|
timestamp: msgData.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
47031
|
-
messageType
|
|
47192
|
+
messageType,
|
|
47193
|
+
roomId: msgData.room_id
|
|
47032
47194
|
};
|
|
47033
47195
|
this.emit("room_message", {
|
|
47034
47196
|
roomId: msgData.room_id,
|
|
47035
47197
|
senderDeviceId: msgData.sender_device_id,
|
|
47036
|
-
plaintext,
|
|
47037
|
-
messageType
|
|
47198
|
+
plaintext: messageText,
|
|
47199
|
+
messageType,
|
|
47038
47200
|
timestamp: msgData.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
47039
47201
|
});
|
|
47040
|
-
this.config.onMessage?.(
|
|
47202
|
+
this.config.onMessage?.(messageText, metadata);
|
|
47041
47203
|
}
|
|
47042
47204
|
/**
|
|
47043
47205
|
* Find the pairwise conversation ID for a given sender in a room.
|
|
@@ -47597,13 +47759,67 @@ async function _handleInbound(params) {
|
|
|
47597
47759
|
});
|
|
47598
47760
|
}
|
|
47599
47761
|
|
|
47762
|
+
// src/gateway-send.ts
|
|
47763
|
+
function resolveBaseUrl(options) {
|
|
47764
|
+
const host = options?.host ?? "127.0.0.1";
|
|
47765
|
+
const port = options?.port ?? (process.env.GATEWAY_SEND_PORT ? Number(process.env.GATEWAY_SEND_PORT) : 18790);
|
|
47766
|
+
return `http://${host}:${port}`;
|
|
47767
|
+
}
|
|
47768
|
+
function friendlyError(err) {
|
|
47769
|
+
if (err instanceof TypeError && String(err.cause ?? err.message).includes("ECONNREFUSED")) {
|
|
47770
|
+
return "Gateway not reachable \u2014 is OpenClaw running?";
|
|
47771
|
+
}
|
|
47772
|
+
return err instanceof Error ? err.message : String(err);
|
|
47773
|
+
}
|
|
47774
|
+
async function sendToOwner(text, options) {
|
|
47775
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
47776
|
+
return { ok: false, error: "Message text must be a non-empty string" };
|
|
47777
|
+
}
|
|
47778
|
+
try {
|
|
47779
|
+
const base = resolveBaseUrl(options);
|
|
47780
|
+
const res = await fetch(`${base}/send`, {
|
|
47781
|
+
method: "POST",
|
|
47782
|
+
headers: { "Content-Type": "application/json" },
|
|
47783
|
+
body: JSON.stringify({ text }),
|
|
47784
|
+
signal: options?.signal
|
|
47785
|
+
});
|
|
47786
|
+
if (!res.ok) {
|
|
47787
|
+
const body = await res.text().catch(() => "");
|
|
47788
|
+
return { ok: false, error: `HTTP ${res.status}${body ? `: ${body}` : ""}` };
|
|
47789
|
+
}
|
|
47790
|
+
return { ok: true };
|
|
47791
|
+
} catch (err) {
|
|
47792
|
+
return { ok: false, error: friendlyError(err) };
|
|
47793
|
+
}
|
|
47794
|
+
}
|
|
47795
|
+
async function checkGateway(options) {
|
|
47796
|
+
try {
|
|
47797
|
+
const base = resolveBaseUrl(options);
|
|
47798
|
+
const res = await fetch(`${base}/status`, { signal: options?.signal });
|
|
47799
|
+
if (!res.ok) {
|
|
47800
|
+
return { ok: false, error: `HTTP ${res.status}` };
|
|
47801
|
+
}
|
|
47802
|
+
const data = await res.json();
|
|
47803
|
+
return {
|
|
47804
|
+
ok: true,
|
|
47805
|
+
state: data.state,
|
|
47806
|
+
deviceId: data.deviceId,
|
|
47807
|
+
sessions: data.sessions
|
|
47808
|
+
};
|
|
47809
|
+
} catch (err) {
|
|
47810
|
+
return { ok: false, error: friendlyError(err) };
|
|
47811
|
+
}
|
|
47812
|
+
}
|
|
47813
|
+
|
|
47600
47814
|
// src/index.ts
|
|
47601
|
-
var VERSION = "0.
|
|
47815
|
+
var VERSION = "0.9.0";
|
|
47602
47816
|
export {
|
|
47603
47817
|
SecureChannel,
|
|
47604
47818
|
VERSION,
|
|
47605
47819
|
agentVaultPlugin,
|
|
47820
|
+
checkGateway,
|
|
47606
47821
|
getActiveChannel,
|
|
47822
|
+
sendToOwner,
|
|
47607
47823
|
setOcRuntime
|
|
47608
47824
|
};
|
|
47609
47825
|
//# sourceMappingURL=index.js.map
|