openclaw-mobile 1.1.1 → 1.1.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/index.ts CHANGED
@@ -576,7 +576,17 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
576
576
  ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} reconnected, resetting E2E state`);
577
577
  }
578
578
 
579
- await initiateHandshake(ctx, state, sessionKey, accountId);
579
+ const sessionE2E = makeE2EState();
580
+ state.e2eSessions.set(sessionKey, sessionE2E);
581
+
582
+ const handshakeMsg = await e2eInit(sessionE2E);
583
+
584
+ // sessionKey를 포함시켜 relay가 올바른 app으로 라우팅하도록 함
585
+ const handshakeWithSession = JSON.stringify({ ...JSON.parse(handshakeMsg), sessionKey });
586
+
587
+ if (state.ws?.readyState === WebSocket.OPEN) {
588
+ state.ws.send(handshakeWithSession);
589
+ }
580
590
  return;
581
591
  }
582
592
 
@@ -591,15 +601,21 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
591
601
  let sessionE2E = state.e2eSessions.get(sessionKey);
592
602
  if (!sessionE2E) {
593
603
  // peer_joined 없이 app이 먼저 handshake를 보낸 경우 (예: plugin 재연결)
594
- await initiateHandshake(ctx, state, sessionKey, accountId);
595
- ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} — sent handshake (reactive, no prior peer_joined)`);
596
- sessionE2E = state.e2eSessions.get(sessionKey); // Re-fetch after initiation
604
+ // 세션을 만들고 plugin의 pubkey를 먼저 전송한 후 ECDH 완성
605
+ sessionE2E = makeE2EState();
606
+ state.e2eSessions.set(sessionKey, sessionE2E);
607
+ const handshakeMsg = await e2eInit(sessionE2E);
608
+ const handshakeWithSession = JSON.stringify({ ...JSON.parse(handshakeMsg), sessionKey });
609
+ if (state.ws?.readyState === WebSocket.OPEN) {
610
+ state.ws.send(handshakeWithSession);
611
+ ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} — sent handshake (reactive, no prior peer_joined)`);
612
+ }
597
613
  }
598
- if (sessionE2E?.ready) {
614
+ if (sessionE2E.ready) {
599
615
  ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} already ready, ignoring duplicate handshake`);
600
616
  return;
601
617
  }
602
- if (!sessionE2E?.localKeyPair) {
618
+ if (!sessionE2E.localKeyPair) {
603
619
  ctx.log?.warn?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} has no local keypair yet, dropping handshake`);
604
620
  return;
605
621
  }
@@ -622,9 +638,9 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
622
638
  }
623
639
  const plaintext = await e2eDecrypt(sessionE2E, msg.nonce, msg.ct);
624
640
  ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] [E2E] Session ${sessionKey} decrypted: ${plaintext.slice(0, 200)}`);
625
-
626
641
  const innerMsg = JSON.parse(plaintext);
627
- innerMsg._connSessionKey = sessionKey;
642
+ innerMsg._connSessionKey = sessionKey; // routing key
643
+ if (!innerMsg.sessionKey) innerMsg.sessionKey = sessionKey;
628
644
  await handleInbound(ctx, accountId, innerMsg);
629
645
  return;
630
646
  }
@@ -634,18 +650,6 @@ async function handleRelayMessage(ctx: any, accountId: string, state: RelayState
634
650
  await handleInbound(ctx, accountId, msg);
635
651
  }
636
652
 
637
- async function initiateHandshake(ctx: any, state: RelayState, sessionKey: string, accountId: string): Promise<void> {
638
- const sessionE2E = makeE2EState();
639
- state.e2eSessions.set(sessionKey, sessionE2E);
640
-
641
- const handshakeMsg = await e2eInit(sessionE2E);
642
- const handshakeWithSession = JSON.stringify({ ...JSON.parse(handshakeMsg), sessionKey });
643
-
644
- if (state.ws?.readyState === WebSocket.OPEN) {
645
- state.ws.send(handshakeWithSession);
646
- }
647
- }
648
-
649
653
  async function handleInbound(ctx: any, accountId: string, msg: any) {
650
654
  ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] handleInbound: type=${msg.type} content=${String(msg.content ?? "").slice(0, 100)}`);
651
655
 
@@ -679,7 +683,7 @@ async function handleInbound(ctx: any, accountId: string, msg: any) {
679
683
 
680
684
  let sessionKey: string;
681
685
  const appSessionKey = msg.sessionKey ? String(msg.sessionKey) : null;
682
- const routingSessionKey = msg._connSessionKey ? String(msg._connSessionKey) : appSessionKey;
686
+ const connSessionKey = msg._connSessionKey ? String(msg._connSessionKey) : appSessionKey;
683
687
 
684
688
  if (appSessionKey) {
685
689
  const mainKey = route.mainSessionKey as string;
@@ -689,7 +693,6 @@ async function handleInbound(ctx: any, accountId: string, msg: any) {
689
693
  } else {
690
694
  sessionKey = route.mainSessionKey;
691
695
  }
692
-
693
696
  const from = `${CHANNEL_ID}:${senderId}`;
694
697
  const to = `user:${senderId}`;
695
698
 
@@ -753,32 +756,40 @@ async function handleInbound(ctx: any, accountId: string, msg: any) {
753
756
  const chunkMode = runtime.channel.text.resolveChunkMode(cfg, CHANNEL_ID, accountId);
754
757
  const chunks = runtime.channel.text.chunkMarkdownTextWithMode(replyText, textLimit, chunkMode);
755
758
 
756
- // Since routingSessionKey maps 1:1 to the App's session ID:
757
- const sessionE2E = routingSessionKey ? relayState.e2eSessions.get(routingSessionKey) : undefined;
758
759
  const replySessionKey = appSessionKey ?? sessionKey;
760
+ const routingSessionKey = connSessionKey ?? sessionKey;
761
+ const sessionE2E = relayState.e2eSessions.get(routingSessionKey);
759
762
 
760
763
  for (const chunk of chunks.length > 0 ? chunks : [replyText]) {
761
764
  if (!chunk) continue;
762
- const plainMsg = JSON.stringify({
763
- type: "message",
764
- role: "assistant",
765
- content: chunk,
766
- sessionKey: replySessionKey,
767
- });
768
-
769
- let outMsg = sessionE2E?.ready
770
- ? await e2eEncrypt(sessionE2E, plainMsg)
771
- : plainMsg;
772
-
773
- try {
774
- const parsedOut = JSON.parse(outMsg);
775
- parsedOut.sessionKey = routingSessionKey;
776
- outMsg = JSON.stringify(parsedOut);
777
- } catch { }
765
+
766
+ let outMsg;
767
+ if (sessionE2E?.ready) {
768
+ const plainMsg = JSON.stringify({
769
+ type: "message",
770
+ role: "assistant",
771
+ content: chunk,
772
+ sessionKey: replySessionKey,
773
+ });
774
+ outMsg = await e2eEncrypt(sessionE2E, plainMsg);
775
+ try {
776
+ const parsedOut = JSON.parse(outMsg);
777
+ parsedOut.sessionKey = routingSessionKey;
778
+ outMsg = JSON.stringify(parsedOut);
779
+ } catch { }
780
+ } else {
781
+ outMsg = JSON.stringify({
782
+ type: "message",
783
+ role: "assistant",
784
+ content: chunk,
785
+ appSessionKey: replySessionKey,
786
+ sessionKey: routingSessionKey,
787
+ });
788
+ }
778
789
 
779
790
  relayState.ws.send(outMsg);
780
791
  }
781
- ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] Reply delivered (${replyText.length} chars) to routingSessionKey=${routingSessionKey}`);
792
+ ctx.log?.info?.(`[${CHANNEL_ID}] [${accountId}] Reply delivered (${replyText.length} chars) to sessionKey=${replySessionKey}`);
782
793
  },
783
794
  onError: (err: any, info: any) => {
784
795
  ctx.log?.error?.(`[${CHANNEL_ID}] [${accountId}] Reply dispatch error (${info?.kind}): ${err}`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-mobile",
3
3
  "name": "OpenClaw Mobile",
4
- "version": "1.1.1",
4
+ "version": "1.1.3",
5
5
  "description": "Mobile app channel for OpenClaw — chat via the OpenClaw Mobile app through a Cloudflare Worker relay.",
6
6
  "channels": [
7
7
  "openclaw-mobile"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-mobile",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "OpenClaw Mobile channel plugin — relay bridge for the OpenClaw Mobile app",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -28,4 +28,4 @@
28
28
  "optional": true
29
29
  }
30
30
  }
31
- }
31
+ }