@keychat-io/keychat-openclaw 0.1.8 → 0.1.10

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
@@ -13,11 +13,11 @@ Your agent becomes a full Keychat citizen: it can receive friend requests, estab
13
13
  ### Option A: OpenClaw plugin (recommended)
14
14
 
15
15
  ```bash
16
- openclaw plugins install @keychat-io/keychat
16
+ openclaw plugins install @keychat-io/keychat-openclaw
17
17
  openclaw gateway restart
18
18
  ```
19
19
 
20
- The bridge binary is auto-downloaded on first start. Supported platforms: macOS (ARM/x64), Linux (x64/ARM64).
20
+ Supported platforms: macOS (ARM/x64), Linux (x64/ARM64).
21
21
 
22
22
  ### Option B: Shell script
23
23
 
@@ -40,31 +40,26 @@ Source code is fully open: [github.com/keychat-io/keychat-openclaw](https://gith
40
40
 
41
41
  ### Upgrade
42
42
 
43
+ If you installed via **Option A**:
44
+
43
45
  ```bash
44
- # npm plugin
45
- openclaw plugins install @keychat-io/keychat@latest
46
+ openclaw plugins install @keychat-io/keychat-openclaw@latest
46
47
  openclaw gateway restart
48
+ ```
47
49
 
48
- # shell script (re-run the same command)
50
+ If you installed via **Option B**:
51
+
52
+ ```bash
49
53
  curl -fsSL https://raw.githubusercontent.com/keychat-io/keychat-openclaw/main/scripts/install.sh | bash
50
54
  ```
51
55
 
52
56
  ### Connect
53
57
 
54
- 1. Run `openclaw status` to find your agent's **npub**
58
+ 1. Run `openclaw status` your agent's **npub** and **QR code** will be displayed
55
59
  2. Open the [Keychat app](https://keychat.io) → tap **Add Contact** on the home page
56
- 3. Paste the agent's npub and confirm
60
+ 3. Scan the QR code, or paste the npub
57
61
  4. The agent will automatically accept the friend request and establish an encrypted session
58
62
 
59
- You can also scan the QR code instead of pasting the npub:
60
-
61
- ```bash
62
- # View QR code in terminal
63
- chafa ~/.openclaw/keychat/qr-default.png
64
- # or generate from npub
65
- qrencode -t ANSIUTF8 "https://www.keychat.io/u/?k=YOUR_NPUB"
66
- ```
67
-
68
63
  ## Configuration
69
64
 
70
65
  All options go under `channels.keychat` in your OpenClaw config:
@@ -11,7 +11,7 @@ Step-by-step instructions to get your OpenClaw agent communicating via Keychat.
11
11
  ## Step 1: Install the Plugin
12
12
 
13
13
  ```bash
14
- openclaw plugins install @keychat-io/keychat
14
+ openclaw plugins install @keychat-io/keychat-openclaw
15
15
  ```
16
16
 
17
17
  This auto-downloads the pre-compiled Rust sidecar binary for your platform. No Rust toolchain needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keychat-io/keychat-openclaw",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Keychat — E2E encrypted chat + Lightning wallet for OpenClaw agents",
5
5
  "license": "AGPL-3.0",
6
6
  "repository": {
@@ -17,6 +17,7 @@
17
17
  "zod": "^4.3.6"
18
18
  },
19
19
  "openclaw": {
20
+ "id": "keychat",
20
21
  "extensions": [
21
22
  "./index.ts"
22
23
  ],
package/src/channel.ts CHANGED
@@ -714,7 +714,7 @@ export const keychatPlugin: ChannelPlugin<ResolvedKeychatAccount> = {
714
714
  ctx.log?.info(`[${account.accountId}] Bridge sidecar started`);
715
715
 
716
716
  // 2. Initialize Signal Protocol DB
717
- const dbPath = `~/.openclaw/keychat/signal-${account.accountId}.db`;
717
+ const dbPath = `~/.openclaw/keychat-signal-${account.accountId}.db`;
718
718
  await bridge.init(dbPath);
719
719
  ctx.log?.info(`[${account.accountId}] Signal DB initialized`);
720
720
 
@@ -797,14 +797,19 @@ export const keychatPlugin: ChannelPlugin<ResolvedKeychatAccount> = {
797
797
  const contactUrl = `https://www.keychat.io/u/?k=${info.pubkey_npub}`;
798
798
  const qrPath = qrCodePath(account.accountId);
799
799
 
800
- // Generate QR code (best-effort)
801
- let qrSaved = false;
800
+ // Generate QR codes (best-effort)
801
+ let qrTerminal = "";
802
+ try {
803
+ const { generateQRTerminal } = await import("./qrcode.js");
804
+ qrTerminal = await generateQRTerminal(contactUrl);
805
+ } catch { /* qrcode not installed, skip */ }
806
+
807
+ // Also save PNG for sharing (best-effort)
802
808
  try {
803
809
  mkdirSync(WORKSPACE_KEYCHAT_DIR, { recursive: true });
804
810
  const QRCode = await import("qrcode");
805
811
  await QRCode.toFile(qrPath, contactUrl, { width: 256 });
806
- qrSaved = true;
807
- } catch { /* qrcode not installed, skip */ }
812
+ } catch { /* skip */ }
808
813
 
809
814
  const cfg = runtime.config.loadConfig();
810
815
  const displayName = resolveDisplayName(cfg, account.accountId, account.name);
@@ -817,7 +822,7 @@ export const keychatPlugin: ChannelPlugin<ResolvedKeychatAccount> = {
817
822
  `\n` +
818
823
  ` 📱 Add contact (tap or scan):\n` +
819
824
  ` ${contactUrl}\n` +
820
- (qrSaved ? `\n 🖼️ QR code: ${qrPath}\n` : ``) +
825
+ (qrTerminal ? `\n${qrTerminal}\n` : ``) +
821
826
  `═══════════════════════════════════════════════════\n`,
822
827
  );
823
828
 
@@ -840,8 +845,10 @@ export const keychatPlugin: ChannelPlugin<ResolvedKeychatAccount> = {
840
845
  activeBridges.set(account.accountId, bridge);
841
846
 
842
847
  // 7. Restore peer sessions and receiving addresses from DB
848
+ console.log(`[keychat] [${account.accountId}] Step 7: restoring peer sessions...`);
843
849
  try {
844
850
  const { mappings } = await bridge.getPeerMappings();
851
+ console.log(`[keychat] [${account.accountId}] Step 7: getPeerMappings returned ${mappings.length} mapping(s)`);
845
852
  if (mappings.length > 0) {
846
853
  ctx.log?.info(`[${account.accountId}] Restored ${mappings.length} peer mapping(s) from DB`);
847
854
  for (const m of mappings) {
@@ -871,7 +878,9 @@ export const keychatPlugin: ChannelPlugin<ResolvedKeychatAccount> = {
871
878
  }
872
879
 
873
880
  // Restore address-to-peer mappings from DB and populate peerSubscribedAddresses
881
+ console.log(`[keychat] [${account.accountId}] Step 7b: getting address mappings...`);
874
882
  const { mappings: addrMappings } = await bridge.getAddressMappings();
883
+ console.log(`[keychat] [${account.accountId}] Step 7b: getAddressMappings returned ${addrMappings.length} mapping(s)`);
875
884
  if (addrMappings.length > 0) {
876
885
  for (const am of addrMappings) {
877
886
  getAddressToPeer(account.accountId).set(am.address, am.peer_nostr_pubkey);
@@ -935,6 +944,7 @@ export const keychatPlugin: ChannelPlugin<ResolvedKeychatAccount> = {
935
944
  }
936
945
  } catch (err) {
937
946
  ctx.log?.error(`[${account.accountId}] Failed to restore sessions from DB: ${err}`);
947
+ console.error(`[keychat] [${account.accountId}] Failed to restore sessions from DB:`, err);
938
948
  }
939
949
 
940
950
  // 8. Restore groups from DB
package/src/paths.ts CHANGED
@@ -19,7 +19,7 @@ export const WORKSPACE_KEYCHAT_DIR = join(HOME, ".openclaw", "workspace", "keych
19
19
 
20
20
  /** Signal DB path for a given account */
21
21
  export function signalDbPath(accountId: string): string {
22
- return join(KEYCHAT_DIR, `signal-${accountId}.db`);
22
+ return join(HOME, ".openclaw", `keychat-signal-${accountId}.db`);
23
23
  }
24
24
 
25
25
  /** QR code image path for a given account */
@@ -1,4 +1,5 @@
1
1
  declare module "qrcode" {
2
2
  export function toDataURL(text: string, options?: Record<string, unknown>): Promise<string>;
3
3
  export function toFile(path: string, text: string, options?: Record<string, unknown>): Promise<void>;
4
+ export function toString(text: string, options?: Record<string, unknown>): Promise<string>;
4
5
  }
package/src/qrcode.ts CHANGED
@@ -7,3 +7,17 @@ export async function generateQRDataUrl(npub: string): Promise<string> {
7
7
  return ""; // QR code generation not available
8
8
  }
9
9
  }
10
+
11
+ /**
12
+ * Generate a QR code as Unicode block characters for terminal display.
13
+ * Uses half-block characters (▀▄█ ) so each text row encodes 2 pixel rows.
14
+ * Can be scanned by phone camera directly from the screen.
15
+ */
16
+ export async function generateQRTerminal(url: string): Promise<string> {
17
+ try {
18
+ const QRCode = await import("qrcode");
19
+ return await QRCode.toString(url, { type: "terminal", small: true });
20
+ } catch {
21
+ return ""; // qrcode not installed
22
+ }
23
+ }