openclaw-app 1.0.8 → 1.0.9

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/README.md CHANGED
@@ -90,7 +90,7 @@ openclaw gateway --port 18789
90
90
 
91
91
  ### 5. Verify
92
92
 
93
- Open the Control UI at http://127.0.0.1:18789/ and navigate to the **OpenClaw Mobile** channel page. You should see:
93
+ Open the Control UI at http://127.0.0.1:18789/ and navigate to the **OpenClaw App** channel page. You should see:
94
94
 
95
95
  - **Running**: Yes
96
96
  - **Configured**: Yes
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OpenClaw Mobile Channel Plugin
2
+ * OpenClaw App Channel Plugin
3
3
  *
4
4
  * Registers "openclaw-app" channel that bridges Gateway <-> CF Worker relay <-> Mobile App.
5
5
  * Plugin runs inside the Gateway process, connects outbound to the relay.
@@ -244,6 +244,8 @@ interface RelayState {
244
244
  /** Previous E2E states kept for decrypting offline-buffered messages
245
245
  * that were encrypted with the old key before the app reconnected. */
246
246
  prevE2eSessions: Map<string, E2EState>;
247
+ /** Buffered pending_flush payloads waiting for the new E2E handshake to complete. */
248
+ pendingFlushQueue: Map<string, string[]>;
247
249
  relayToken: string;
248
250
  }
249
251
 
@@ -265,6 +267,7 @@ function getRelayState(accountId: string): RelayState {
265
267
  gatewayCtx: null,
266
268
  e2eSessions: new Map(),
267
269
  prevE2eSessions: new Map(),
270
+ pendingFlushQueue: new Map(),
268
271
  relayToken: "",
269
272
  };
270
273
  relayStates.set(accountId, state);
@@ -367,9 +370,9 @@ const channel = {
367
370
  id: CHANNEL_ID,
368
371
  meta: {
369
372
  id: CHANNEL_ID,
370
- label: "OpenClaw Mobile",
371
- selectionLabel: "OpenClaw Mobile App",
372
- blurb: "Chat via the OpenClaw Mobile app through a relay.",
373
+ label: "OpenClaw App",
374
+ selectionLabel: "OpenClaw App App",
375
+ blurb: "Chat via the OpenClaw App app through a relay.",
373
376
  detailLabel: "Mobile App",
374
377
  aliases: ["mobile"],
375
378
  },
@@ -561,6 +564,7 @@ function cleanupRelay(state: RelayState) {
561
564
  }
562
565
  state.e2eSessions.clear();
563
566
  state.prevE2eSessions.clear();
567
+ state.pendingFlushQueue.clear();
564
568
  }
565
569
 
566
570
  function connectRelay(ctx: any, account: ResolvedAccount) {
@@ -650,6 +654,41 @@ function scheduleReconnect(ctx: any, account: ResolvedAccount) {
650
654
  }, RECONNECT_DELAY);
651
655
  }
652
656
 
657
+ async function processPendingFlush(
658
+ ctx: any, accountId: string, state: RelayState, sessionKey: string, messages: string[]
659
+ ): Promise<void> {
660
+ const oldE2E = state.prevE2eSessions.get(sessionKey);
661
+ const newE2E = state.e2eSessions.get(sessionKey);
662
+ if (!oldE2E?.ready) {
663
+ ctx.log?.warn?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: no old key for session ${sessionKey}, dropping ${messages.length} message(s)`);
664
+ return;
665
+ }
666
+ if (!newE2E?.ready) {
667
+ ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: new E2E not ready, queueing for session ${sessionKey}`);
668
+ state.pendingFlushQueue.set(sessionKey, messages);
669
+ return;
670
+ }
671
+ ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: re-encrypting ${messages.length} message(s) for session ${sessionKey}`);
672
+ for (const raw of messages) {
673
+ try {
674
+ const parsed = JSON.parse(raw);
675
+ if (parsed.type !== "encrypted" || !parsed.nonce || !parsed.ct) {
676
+ if (!parsed.sessionKey) parsed.sessionKey = sessionKey;
677
+ state.ws?.send(JSON.stringify(parsed));
678
+ continue;
679
+ }
680
+ const plaintext = await e2eDecrypt(oldE2E, parsed.nonce, parsed.ct);
681
+ const reEncrypted = JSON.parse(await e2eEncrypt(newE2E, plaintext));
682
+ reEncrypted.sessionKey = sessionKey;
683
+ state.ws?.send(JSON.stringify(reEncrypted));
684
+ } catch (e) {
685
+ ctx.log?.warn?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: failed to re-encrypt: ${e}`);
686
+ }
687
+ }
688
+ state.prevE2eSessions.delete(sessionKey);
689
+ state.pendingFlushQueue.delete(sessionKey);
690
+ }
691
+
653
692
  async function handleRelayMessage(ctx: any, accountId: string, state: RelayState, raw: string): Promise<void> {
654
693
  // Skip ping/pong
655
694
  if (raw === "ping" || raw === "pong") return;
@@ -706,8 +745,6 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
706
745
  }
707
746
 
708
747
  // Relay forwards buffered offline messages for re-encryption.
709
- // Each message was encrypted with the old E2E key; we decrypt with the
710
- // preserved old key and re-encrypt with the current (new) session key.
711
748
  if (msg.type === "pending_flush") {
712
749
  const sessionKey = msg.sessionKey as string | undefined;
713
750
  const messages = msg.messages as string[] | undefined;
@@ -715,36 +752,7 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
715
752
  ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: nothing to flush`);
716
753
  return;
717
754
  }
718
- const oldE2E = state.prevE2eSessions.get(sessionKey);
719
- const newE2E = state.e2eSessions.get(sessionKey);
720
- if (!oldE2E?.ready) {
721
- ctx.log?.warn?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: no old key for session ${sessionKey}, dropping ${messages.length} message(s)`);
722
- return;
723
- }
724
- if (!newE2E?.ready) {
725
- ctx.log?.warn?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: new E2E not ready for session ${sessionKey}, dropping`);
726
- return;
727
- }
728
- ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: re-encrypting ${messages.length} message(s) for session ${sessionKey}`);
729
- for (const raw of messages) {
730
- try {
731
- const parsed = JSON.parse(raw);
732
- if (parsed.type !== "encrypted" || !parsed.nonce || !parsed.ct) {
733
- // Not encrypted — forward as-is with sessionKey
734
- if (!parsed.sessionKey) parsed.sessionKey = sessionKey;
735
- state.ws?.send(JSON.stringify(parsed));
736
- continue;
737
- }
738
- const plaintext = await e2eDecrypt(oldE2E, parsed.nonce, parsed.ct);
739
- const reEncrypted = JSON.parse(await e2eEncrypt(newE2E, plaintext));
740
- reEncrypted.sessionKey = sessionKey;
741
- state.ws?.send(JSON.stringify(reEncrypted));
742
- } catch (e) {
743
- ctx.log?.warn?.(`[${CHANNEL_ID}] [${accountId}] [E2E] pending_flush: failed to re-encrypt: ${e}`);
744
- }
745
- }
746
- // Old key no longer needed after flush
747
- state.prevE2eSessions.delete(sessionKey);
755
+ await processPendingFlush(ctx, accountId, state, sessionKey, messages);
748
756
  return;
749
757
  }
750
758
 
@@ -828,6 +836,13 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
828
836
  }
829
837
  await e2eHandleHandshake(sessionE2E, peerPubKey);
830
838
  ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} handshake complete`);
839
+
840
+ // Process any queued pending_flush that arrived before this handshake completed
841
+ const queued = state.pendingFlushQueue.get(sessionKey);
842
+ if (queued && queued.length > 0) {
843
+ ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Processing queued pending_flush for session ${sessionKey}`);
844
+ await processPendingFlush(ctx, accountId, state, sessionKey, queued);
845
+ }
831
846
  return;
832
847
  }
833
848
 
@@ -927,7 +942,7 @@ async function handleInbound(ctx: any, accountId: string, msg: any) {
927
942
  );
928
943
 
929
944
  const body = runtime.channel.reply.formatInboundEnvelope({
930
- channel: "OpenClaw Mobile",
945
+ channel: "OpenClaw App",
931
946
  from: `${senderName} (mobile)`,
932
947
  body: text,
933
948
  chatType: "direct",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "openclaw-app",
3
3
  "name": "OpenClaw App",
4
- "version": "1.0.8",
5
- "description": "Mobile app channel for OpenClaw — chat via the OpenClaw Mobile app through a Cloudflare Worker relay.",
4
+ "version": "1.0.9",
5
+ "description": "Mobile app channel for OpenClaw — chat via the OpenClaw App app through a Cloudflare Worker relay.",
6
6
  "channels": [
7
7
  "openclaw-app"
8
8
  ],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "openclaw-app",
3
- "version": "1.0.8",
4
- "description": "OpenClaw Mobile channel plugin — relay bridge for the OpenClaw Mobile app",
3
+ "version": "1.0.9",
4
+ "description": "OpenClaw App channel plugin — relay bridge for the OpenClaw App app",
5
5
  "main": "index.ts",
6
6
  "type": "module",
7
7
  "openclaw": {