@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.
@@ -1,12 +1,2 @@
1
- import type { EncryptedMessage } from "@agentvault/crypto";
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":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAElD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEpD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAErD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAKD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,gBAAgB,GACpB,gBAAgB,CAgBlB;AAED,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,gBAAgB,GAC1B,gBAAgB,CAelB"}
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 declare const VERSION = "0.7.0";
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
@@ -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;AAExF,eAAO,MAAM,OAAO,UAAU,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
- // src/crypto-helpers.ts
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
- room_id: roomId,
45664
- recipients,
45665
- message_type: messageType
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
- const plaintext = session.ratchet.decrypt(encrypted);
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: msgData.message_type ?? "text"
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: msgData.message_type ?? "text",
47198
+ plaintext: messageText,
47199
+ messageType,
47038
47200
  timestamp: msgData.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
47039
47201
  });
47040
- this.config.onMessage?.(plaintext, metadata);
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.7.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