@keychat-io/keychat-openclaw 0.1.14 → 0.1.15
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 +47 -108
- package/index.ts +84 -1
- package/package.json +1 -1
- package/scripts/install.sh +3 -2
- package/scripts/postinstall.mjs +20 -5
- package/scripts/setup.sh +80 -0
package/README.md
CHANGED
|
@@ -10,115 +10,94 @@ Your agent becomes a full Keychat citizen: it can receive friend requests, estab
|
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
-
### Option A: OpenClaw plugin (recommended)
|
|
14
|
-
|
|
15
13
|
```bash
|
|
16
14
|
openclaw plugins install @keychat-io/keychat-openclaw
|
|
17
15
|
openclaw gateway restart
|
|
18
16
|
```
|
|
19
17
|
|
|
18
|
+
That's it. The plugin automatically downloads the bridge binary and initializes the config on first load.
|
|
19
|
+
|
|
20
20
|
Supported platforms: macOS (ARM/x64), Linux (x64/ARM64).
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Alternatively, install via shell script:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
curl -fsSL https://raw.githubusercontent.com/keychat-io/keychat-openclaw/main/scripts/install.sh | bash
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
This clones the repo, downloads the binary, registers the plugin, and restarts the gateway in one step.
|
|
29
|
-
|
|
30
28
|
### Security Warnings
|
|
31
29
|
|
|
32
30
|
During installation, OpenClaw's security scanner may show three warnings. All are expected:
|
|
33
31
|
|
|
34
32
|
| Warning | Reason |
|
|
35
33
|
|---------|--------|
|
|
36
|
-
| Shell command execution (bridge-client.ts) |
|
|
37
|
-
| Shell command execution (keychain.ts) |
|
|
38
|
-
| Shell command execution (notify.ts) |
|
|
34
|
+
| Shell command execution (bridge-client.ts) | Spawns a Rust sidecar for Signal Protocol and MLS encryption. |
|
|
35
|
+
| Shell command execution (keychain.ts) | Stores identity mnemonics in the OS keychain (macOS Keychain / Linux libsecret). |
|
|
36
|
+
| Shell command execution (notify.ts) | Notifies the agent on startup so it can send the Keychat ID and QR code to the user. |
|
|
39
37
|
|
|
40
38
|
Source code is fully open: [github.com/keychat-io/keychat-openclaw](https://github.com/keychat-io/keychat-openclaw)
|
|
41
39
|
|
|
42
40
|
### Upgrade
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Or manually:
|
|
47
|
-
|
|
48
|
-
If you installed via **Option A**:
|
|
42
|
+
Tell your agent "upgrade keychat" in any chat, or manually:
|
|
49
43
|
|
|
50
44
|
```bash
|
|
51
45
|
openclaw plugins install @keychat-io/keychat-openclaw@latest
|
|
52
46
|
openclaw gateway restart
|
|
53
47
|
```
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
## Add Your Agent as a Keychat Contact
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
curl -fsSL https://raw.githubusercontent.com/keychat-io/keychat-openclaw/main/scripts/install.sh | bash
|
|
59
|
-
```
|
|
51
|
+
After `openclaw gateway restart`, the agent will send you its **Keychat ID**, **contact link**, and **QR code** in your active chat (Telegram, webchat, etc.):
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
```
|
|
54
|
+
🔑 Keychat ID: npub1...
|
|
55
|
+
📱 Add contact: https://www.keychat.io/u/?k=npub1...
|
|
56
|
+
🖼️ QR code image
|
|
57
|
+
```
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
2. Open the [Keychat app](https://keychat.io) → tap **Add Contact** on the home page
|
|
65
|
-
3. Scan the QR code, or paste the npub
|
|
66
|
-
4. The agent will automatically accept the friend request and establish an encrypted session
|
|
59
|
+
Open the [Keychat app](https://keychat.io) → tap the link, paste the npub, or scan the QR code to add as contact. If `dmPolicy` is `open` (default after auto-init), the agent accepts immediately.
|
|
67
60
|
|
|
68
61
|
## Configuration
|
|
69
62
|
|
|
70
|
-
All options go under `channels.keychat` in your OpenClaw config:
|
|
63
|
+
All options go under `channels.keychat` in your OpenClaw config (`~/.openclaw/openclaw.json`):
|
|
71
64
|
|
|
72
65
|
| Option | Type | Default | Description |
|
|
73
66
|
| ------------------ | -------- | ---------------------------- | --------------------------------------------------------- |
|
|
74
|
-
| `enabled` | boolean | `
|
|
67
|
+
| `enabled` | boolean | `true` | Enable/disable the Keychat channel |
|
|
75
68
|
| `name` | string | — | Display name for this account |
|
|
76
69
|
| `relays` | string[] | `["wss://relay.keychat.io"]` | Nostr relay WebSocket URLs |
|
|
77
|
-
| `dmPolicy` | enum | `"
|
|
70
|
+
| `dmPolicy` | enum | `"open"` | Access policy: `pairing`, `allowlist`, `open`, `disabled` |
|
|
78
71
|
| `allowFrom` | string[] | `[]` | Allowed sender pubkeys (npub or hex) |
|
|
79
|
-
| `mnemonic` | string | — | Identity mnemonic (auto-generated, stored in keychain) |
|
|
80
|
-
| `publicKey` | string | — | Derived hex public key (read-only) |
|
|
81
|
-
| `npub` | string | — | Derived bech32 npub (read-only) |
|
|
82
72
|
| `lightningAddress` | string | — | Lightning address for receiving payments |
|
|
83
73
|
| `nwcUri` | string | — | Nostr Wallet Connect URI for wallet access |
|
|
84
|
-
| `markdown` | object | — | Markdown formatting overrides |
|
|
85
74
|
|
|
86
75
|
### DM Policies
|
|
87
76
|
|
|
88
|
-
- **`
|
|
77
|
+
- **`open`**: Anyone can message the agent (default)
|
|
78
|
+
- **`pairing`**: New contacts require owner approval via OpenClaw
|
|
89
79
|
- **`allowlist`**: Only pubkeys in `allowFrom` can communicate
|
|
90
|
-
- **`open`**: Anyone can message the agent
|
|
91
80
|
- **`disabled`**: No inbound messages accepted
|
|
92
81
|
|
|
93
82
|
## Lightning Wallet
|
|
94
83
|
|
|
95
|
-
Keychat supports Lightning payments:
|
|
96
|
-
|
|
97
84
|
### Lightning Address (receive-only)
|
|
98
85
|
|
|
99
|
-
Configure a Lightning address so your agent can generate invoices:
|
|
100
|
-
|
|
101
86
|
```json
|
|
102
|
-
{
|
|
103
|
-
"lightningAddress": "user@walletofsatoshi.com"
|
|
104
|
-
}
|
|
87
|
+
{ "lightningAddress": "user@walletofsatoshi.com" }
|
|
105
88
|
```
|
|
106
89
|
|
|
107
|
-
The agent can create invoices via LNURL-pay protocol. Note: payment verification depends on the provider (some don't support verify URLs).
|
|
108
|
-
|
|
109
90
|
### Nostr Wallet Connect (NWC)
|
|
110
91
|
|
|
111
|
-
For full wallet access (create invoices, check balance, verify payments)
|
|
92
|
+
For full wallet access (create invoices, check balance, verify payments):
|
|
112
93
|
|
|
113
94
|
```json
|
|
114
|
-
{
|
|
115
|
-
"nwcUri": "nostr+walletconnect://pubkey?relay=wss://...&secret=..."
|
|
116
|
-
}
|
|
95
|
+
{ "nwcUri": "nostr+walletconnect://pubkey?relay=wss://...&secret=..." }
|
|
117
96
|
```
|
|
118
97
|
|
|
119
98
|
Generate an NWC connection string from your wallet app (Keychat, Alby Hub, Mutiny, Coinos, etc.).
|
|
120
99
|
|
|
121
|
-
**Security note**: The agent can receive payments freely. Outbound payments require owner approval
|
|
100
|
+
**Security note**: The agent can receive payments freely. Outbound payments require owner approval.
|
|
122
101
|
|
|
123
102
|
## Architecture
|
|
124
103
|
|
|
@@ -133,71 +112,32 @@ Generate an NWC connection string from your wallet app (Keychat, Alby Hub, Mutin
|
|
|
133
112
|
└────────────────────┘
|
|
134
113
|
```
|
|
135
114
|
|
|
136
|
-
- **TypeScript plugin
|
|
137
|
-
- **Rust sidecar
|
|
138
|
-
- **Communication**: JSON-RPC over stdin/stdout
|
|
139
|
-
- **Encryption**: Signal Protocol (Double Ratchet)
|
|
115
|
+
- **TypeScript plugin**: OpenClaw channel integration, routing, pairing, message dispatch
|
|
116
|
+
- **Rust sidecar**: Signal Protocol sessions, Nostr transport, encryption/decryption
|
|
117
|
+
- **Communication**: JSON-RPC over stdin/stdout
|
|
118
|
+
- **Encryption**: Signal Protocol (Double Ratchet) with forward and backward secrecy
|
|
140
119
|
- **Transport**: Nostr relays (kind:4 DMs + kind:1059 Gift Wrap for friend requests)
|
|
141
120
|
|
|
142
|
-
## Pairing
|
|
143
|
-
|
|
144
|
-
1. Agent starts and logs its **npub** and **contact URL**
|
|
145
|
-
2. User opens the URL or scans the QR code in the Keychat app
|
|
146
|
-
3. Keychat app sends a **friend request** (Gift Wrap, kind:1059)
|
|
147
|
-
4. Agent processes the hello, establishes Signal session, and replies
|
|
148
|
-
5. If `dmPolicy` is `pairing`, the owner must approve via `openclaw pair approve keychat <pubkey>`
|
|
149
|
-
6. Once approved, full bidirectional encrypted chat is established
|
|
150
|
-
|
|
151
121
|
## Security
|
|
152
122
|
|
|
153
123
|
- **E2E Encryption**: All messages encrypted with Signal Protocol — relay operators cannot read content
|
|
154
|
-
- **Forward Secrecy**:
|
|
155
|
-
- **Backward Secrecy**: New messages use fresh keys after each exchange
|
|
124
|
+
- **Forward & Backward Secrecy**: Double Ratchet ensures compromising current keys reveals neither past nor future messages
|
|
156
125
|
- **Sovereign Identity**: Agent generates its own keypair — no third-party identity provider
|
|
157
|
-
- **Key Storage**: Mnemonic stored in system keychain (macOS Keychain, Linux secret service)
|
|
158
|
-
- **
|
|
159
|
-
- **Receiving Address Rotation**: Ratchet-derived addresses rotate per message, preventing traffic analysis
|
|
126
|
+
- **Key Storage**: Mnemonic stored in system keychain (macOS Keychain, Linux secret service)
|
|
127
|
+
- **Sending Address Rotation**: Each outbound message uses a fresh Nostr keypair, preventing metadata correlation
|
|
128
|
+
- **Receiving Address Rotation**: Ratchet-derived addresses rotate almost per message, preventing traffic analysis
|
|
160
129
|
|
|
161
130
|
## Troubleshooting
|
|
162
131
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
- Check logs for startup errors
|
|
168
|
-
|
|
169
|
-
### Relay connection issues
|
|
170
|
-
|
|
171
|
-
- Verify relay URLs are correct WebSocket endpoints (`wss://...`)
|
|
172
|
-
- Test relay connectivity: `websocat wss://relay.keychat.io`
|
|
173
|
-
- Try alternative relays
|
|
174
|
-
|
|
175
|
-
### Session corruption
|
|
176
|
-
|
|
177
|
-
- If messages fail to decrypt, the plugin will automatically warn the peer
|
|
178
|
-
- The peer should re-add the agent as a contact to establish a new session
|
|
179
|
-
- As a last resort, delete the Signal DB: `rm ~/.openclaw/keychat/signal-default.db` and restart
|
|
180
|
-
|
|
181
|
-
### Messages not delivered
|
|
182
|
-
|
|
183
|
-
- Check if the bridge is responsive: look for health check logs
|
|
184
|
-
- The plugin queues failed messages (up to 100) and retries every 30s
|
|
185
|
-
- Pending messages are also flushed after bridge restart
|
|
186
|
-
|
|
187
|
-
### No QR code generated
|
|
188
|
-
|
|
189
|
-
- Install the `qrcode` npm package: `npm install qrcode`
|
|
190
|
-
- The contact URL in logs works without QR code
|
|
132
|
+
- **Bridge not starting**: Check `ls ~/.openclaw/extensions/keychat-openclaw/bridge/target/release/keychat-openclaw`. If missing, restart gateway (auto-downloads) or build from source: `cd bridge && cargo build --release`
|
|
133
|
+
- **Relay issues**: Verify relay URLs (`wss://...`), try alternative relays
|
|
134
|
+
- **Decryption errors**: Peer should delete old contact and re-add the agent
|
|
135
|
+
- **Messages not delivered**: Plugin queues failed messages (up to 100) and retries every 30s
|
|
191
136
|
|
|
192
137
|
## Development
|
|
193
138
|
|
|
194
|
-
### Building
|
|
195
|
-
|
|
196
139
|
```bash
|
|
197
|
-
# Build the Rust sidecar
|
|
198
140
|
cd bridge && cargo build --release
|
|
199
|
-
|
|
200
|
-
# Run tests
|
|
201
141
|
cargo test
|
|
202
142
|
```
|
|
203
143
|
|
|
@@ -205,24 +145,23 @@ cargo test
|
|
|
205
145
|
|
|
206
146
|
```
|
|
207
147
|
├── src/
|
|
208
|
-
│ ├── channel.ts # Main channel plugin
|
|
209
|
-
│ ├── bridge-client.ts #
|
|
148
|
+
│ ├── channel.ts # Main channel plugin
|
|
149
|
+
│ ├── bridge-client.ts # RPC client for Rust sidecar
|
|
210
150
|
│ ├── config-schema.ts # Zod config schema
|
|
211
151
|
│ ├── keychain.ts # System keychain integration
|
|
212
|
-
│ ├── lightning.ts #
|
|
213
|
-
│ ├── nwc.ts # Nostr Wallet Connect (NIP-47)
|
|
152
|
+
│ ├── lightning.ts # LNURL-pay support
|
|
153
|
+
│ ├── nwc.ts # Nostr Wallet Connect (NIP-47)
|
|
214
154
|
│ ├── media.ts # Blossom media encryption/upload
|
|
215
155
|
│ ├── qrcode.ts # QR code generation
|
|
216
156
|
│ ├── runtime.ts # Plugin runtime accessor
|
|
217
157
|
│ └── types.ts # Account types and resolvers
|
|
218
|
-
├── bridge/
|
|
219
|
-
│
|
|
220
|
-
│
|
|
221
|
-
│
|
|
222
|
-
│
|
|
223
|
-
│
|
|
224
|
-
│
|
|
225
|
-
│ └── transport.rs # Nostr relay transport
|
|
158
|
+
├── bridge/src/
|
|
159
|
+
│ ├── main.rs # Sidecar entry point
|
|
160
|
+
│ ├── rpc.rs # JSON-RPC dispatch
|
|
161
|
+
│ ├── signal.rs # Signal Protocol manager
|
|
162
|
+
│ ├── protocol.rs # Keychat protocol types
|
|
163
|
+
│ ├── mls.rs # MLS large group support
|
|
164
|
+
│ └── transport.rs # Nostr relay transport
|
|
226
165
|
├── scripts/
|
|
227
166
|
│ └── install.sh # One-line installer
|
|
228
167
|
├── index.ts # Plugin entry point
|
package/index.ts
CHANGED
|
@@ -2,7 +2,87 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
|
2
2
|
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
3
|
import { keychatPlugin, getAgentKeychatId, getAgentKeychatUrl, getAllAgentContacts } from "./src/channel.js";
|
|
4
4
|
import { setKeychatRuntime } from "./src/runtime.js";
|
|
5
|
-
import { existsSync } from "node:fs";
|
|
5
|
+
import { existsSync, mkdirSync, chmodSync, writeFileSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join, dirname } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import https from "node:https";
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
/** Download a URL following redirects, return a Buffer. */
|
|
14
|
+
function downloadBinary(url: string): Promise<Buffer> {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
https.get(url, (res) => {
|
|
17
|
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
18
|
+
return downloadBinary(res.headers.location).then(resolve, reject);
|
|
19
|
+
}
|
|
20
|
+
if (res.statusCode !== 200) {
|
|
21
|
+
return reject(new Error(`HTTP ${res.statusCode}`));
|
|
22
|
+
}
|
|
23
|
+
const chunks: Buffer[] = [];
|
|
24
|
+
res.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
25
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
26
|
+
res.on("error", reject);
|
|
27
|
+
}).on("error", reject);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Ensure bridge binary exists, download if missing. */
|
|
32
|
+
async function ensureBinary(): Promise<void> {
|
|
33
|
+
const binaryDir = join(__dirname, "bridge", "target", "release");
|
|
34
|
+
const binaryPath = join(binaryDir, "keychat-openclaw");
|
|
35
|
+
|
|
36
|
+
if (existsSync(binaryPath)) return;
|
|
37
|
+
|
|
38
|
+
const platform = process.platform;
|
|
39
|
+
const arch = process.arch;
|
|
40
|
+
const artifacts: Record<string, string> = {
|
|
41
|
+
"darwin-arm64": "keychat-openclaw-darwin-arm64",
|
|
42
|
+
"darwin-x64": "keychat-openclaw-darwin-x64",
|
|
43
|
+
"linux-x64": "keychat-openclaw-linux-x64",
|
|
44
|
+
"linux-arm64": "keychat-openclaw-linux-arm64",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const artifact = artifacts[`${platform}-${arch}`];
|
|
48
|
+
if (!artifact) {
|
|
49
|
+
console.warn(`[keychat] No pre-compiled binary for ${platform}-${arch}. Build from source: cd bridge && cargo build --release`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const url = `https://github.com/keychat-io/keychat-openclaw/releases/latest/download/${artifact}`;
|
|
54
|
+
console.log(`[keychat] Bridge binary not found, downloading ${artifact}...`);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
mkdirSync(binaryDir, { recursive: true });
|
|
58
|
+
const buffer = await downloadBinary(url);
|
|
59
|
+
writeFileSync(binaryPath, buffer);
|
|
60
|
+
chmodSync(binaryPath, 0o755);
|
|
61
|
+
console.log("[keychat] ✅ Bridge binary installed");
|
|
62
|
+
} catch (err: any) {
|
|
63
|
+
console.warn(`[keychat] Binary download failed: ${err.message}`);
|
|
64
|
+
console.warn("[keychat] Build from source: cd bridge && cargo build --release");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Ensure channels.keychat exists in openclaw.json config. */
|
|
69
|
+
function ensureConfig(): void {
|
|
70
|
+
const configPath = join(homedir(), ".openclaw", "openclaw.json");
|
|
71
|
+
try {
|
|
72
|
+
let config: any = {};
|
|
73
|
+
if (existsSync(configPath)) {
|
|
74
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
75
|
+
}
|
|
76
|
+
if (config.channels?.keychat) return;
|
|
77
|
+
|
|
78
|
+
if (!config.channels) config.channels = {};
|
|
79
|
+
config.channels.keychat = { enabled: true, dmPolicy: "open" };
|
|
80
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
81
|
+
console.log("[keychat] ✅ Config initialized (channels.keychat.enabled = true)");
|
|
82
|
+
} catch (err: any) {
|
|
83
|
+
console.warn(`[keychat] Could not auto-configure: ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
6
86
|
|
|
7
87
|
const plugin = {
|
|
8
88
|
id: "keychat",
|
|
@@ -14,6 +94,9 @@ const plugin = {
|
|
|
14
94
|
register(api: OpenClawPluginApi) {
|
|
15
95
|
console.log("[keychat] register() called");
|
|
16
96
|
try {
|
|
97
|
+
// Auto-setup: download binary + init config on first load
|
|
98
|
+
ensureConfig();
|
|
99
|
+
ensureBinary().catch((err) => console.warn("[keychat] ensureBinary error:", err));
|
|
17
100
|
setKeychatRuntime(api.runtime);
|
|
18
101
|
console.log("[keychat] runtime set, registering channel...");
|
|
19
102
|
api.registerChannel({ plugin: keychatPlugin });
|
package/package.json
CHANGED
package/scripts/install.sh
CHANGED
|
@@ -136,7 +136,8 @@ fi
|
|
|
136
136
|
# ── Restart gateway ──
|
|
137
137
|
echo ""
|
|
138
138
|
echo "🔄 Restarting gateway..."
|
|
139
|
-
openclaw gateway
|
|
139
|
+
openclaw gateway install 2>&1 || true
|
|
140
|
+
openclaw gateway start 2>&1 || true
|
|
140
141
|
|
|
141
142
|
# ── Done ──
|
|
142
143
|
echo ""
|
|
@@ -149,6 +150,6 @@ echo "To connect: open the Keychat app and scan the QR code."
|
|
|
149
150
|
echo ""
|
|
150
151
|
echo "View QR code in terminal:"
|
|
151
152
|
echo " chafa ~/.openclaw/keychat/qr-default.png"
|
|
152
|
-
echo " # or: qrencode -t ANSIUTF8 \"\$(openclaw status 2>/dev/null | grep -
|
|
153
|
+
echo " # or: qrencode -t ANSIUTF8 \"\$(openclaw status 2>/dev/null | grep -o 'npub[a-z0-9]*' | head -1 | xargs -I{} echo 'https://www.keychat.io/u/?k={}')\""
|
|
153
154
|
echo ""
|
|
154
155
|
echo "Docs: https://github.com/$REPO"
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Runs automatically after `npm install` / `openclaw plugins install`.
|
|
5
5
|
* Uses native fetch/https — no child_process dependency.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, mkdirSync, chmodSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { existsSync, mkdirSync, chmodSync, writeFileSync, readFileSync } from "node:fs";
|
|
8
8
|
import { join, dirname } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import https from "node:https";
|
|
@@ -14,11 +14,26 @@ const REPO = "keychat-io/keychat-openclaw";
|
|
|
14
14
|
const BINARY_DIR = join(__dirname, "..", "bridge", "target", "release");
|
|
15
15
|
const BINARY_PATH = join(BINARY_DIR, "keychat-openclaw");
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
import { statSync } from "node:fs";
|
|
18
|
+
|
|
19
|
+
// Read expected version from package.json
|
|
20
|
+
const pkgPath = join(__dirname, "..", "package.json");
|
|
21
|
+
const pkgVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
|
|
22
|
+
const versionFile = join(BINARY_DIR, ".version");
|
|
23
|
+
|
|
24
|
+
const currentVersion = existsSync(versionFile)
|
|
25
|
+
? readFileSync(versionFile, "utf-8").trim()
|
|
26
|
+
: null;
|
|
27
|
+
|
|
28
|
+
if (existsSync(BINARY_PATH) && currentVersion === pkgVersion) {
|
|
29
|
+
console.log(`[keychat] Binary already exists (v${pkgVersion}), skipping download`);
|
|
19
30
|
process.exit(0);
|
|
20
31
|
}
|
|
21
32
|
|
|
33
|
+
if (existsSync(BINARY_PATH)) {
|
|
34
|
+
console.log(`[keychat] Binary exists but version mismatch (${currentVersion || "unknown"} → ${pkgVersion}), re-downloading...`);
|
|
35
|
+
}
|
|
36
|
+
|
|
22
37
|
const platform = process.platform; // darwin, linux
|
|
23
38
|
const arch = process.arch; // arm64, x64
|
|
24
39
|
|
|
@@ -64,7 +79,8 @@ try {
|
|
|
64
79
|
const buffer = await download(url);
|
|
65
80
|
writeFileSync(BINARY_PATH, buffer);
|
|
66
81
|
chmodSync(BINARY_PATH, 0o755);
|
|
67
|
-
|
|
82
|
+
writeFileSync(versionFile, pkgVersion + "\n");
|
|
83
|
+
console.log(`[keychat] ✅ Binary installed (v${pkgVersion})`);
|
|
68
84
|
} catch (err) {
|
|
69
85
|
console.warn(`[keychat] Download failed: ${err.message}`);
|
|
70
86
|
console.warn("[keychat] Build from source: cd bridge && cargo build --release");
|
|
@@ -72,7 +88,6 @@ try {
|
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
// Auto-initialize config if channels.keychat not set
|
|
75
|
-
import { readFileSync } from "node:fs";
|
|
76
91
|
import { homedir } from "node:os";
|
|
77
92
|
|
|
78
93
|
const configPath = join(homedir(), ".openclaw", "openclaw.json");
|
package/scripts/setup.sh
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Keychat OpenClaw plugin setup
|
|
3
|
+
# Downloads the bridge binary and initializes config.
|
|
4
|
+
# Run after: openclaw plugins install @keychat-io/keychat-openclaw
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# --- Locate plugin directory ---
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
|
|
10
|
+
|
|
11
|
+
# Also check common install locations
|
|
12
|
+
if [ ! -f "$PLUGIN_DIR/package.json" ]; then
|
|
13
|
+
for d in \
|
|
14
|
+
"$HOME/.openclaw/extensions/keychat-openclaw" \
|
|
15
|
+
"$HOME/.openclaw/plugins/@keychat-io/keychat-openclaw"; do
|
|
16
|
+
if [ -f "$d/package.json" ]; then
|
|
17
|
+
PLUGIN_DIR="$d"
|
|
18
|
+
break
|
|
19
|
+
fi
|
|
20
|
+
done
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
BINARY_DIR="$PLUGIN_DIR/bridge/target/release"
|
|
24
|
+
BINARY_PATH="$BINARY_DIR/keychat-openclaw"
|
|
25
|
+
|
|
26
|
+
# --- Download binary ---
|
|
27
|
+
if [ -f "$BINARY_PATH" ]; then
|
|
28
|
+
echo "[keychat] ✅ Binary already exists: $BINARY_PATH"
|
|
29
|
+
else
|
|
30
|
+
REPO="keychat-io/keychat-openclaw"
|
|
31
|
+
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
32
|
+
ARCH="$(uname -m)"
|
|
33
|
+
|
|
34
|
+
case "$OS-$ARCH" in
|
|
35
|
+
darwin-arm64) ARTIFACT="keychat-openclaw-darwin-arm64" ;;
|
|
36
|
+
darwin-x86_64) ARTIFACT="keychat-openclaw-darwin-x64" ;;
|
|
37
|
+
linux-x86_64) ARTIFACT="keychat-openclaw-linux-x64" ;;
|
|
38
|
+
linux-aarch64) ARTIFACT="keychat-openclaw-linux-arm64" ;;
|
|
39
|
+
*)
|
|
40
|
+
echo "[keychat] ❌ No pre-compiled binary for $OS-$ARCH"
|
|
41
|
+
echo "[keychat] Build from source: cd $PLUGIN_DIR/bridge && cargo build --release"
|
|
42
|
+
exit 1
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
|
|
46
|
+
URL="https://github.com/$REPO/releases/latest/download/$ARTIFACT"
|
|
47
|
+
echo "[keychat] Downloading $ARTIFACT..."
|
|
48
|
+
mkdir -p "$BINARY_DIR"
|
|
49
|
+
curl -fsSL -o "$BINARY_PATH" "$URL"
|
|
50
|
+
chmod +x "$BINARY_PATH"
|
|
51
|
+
echo "[keychat] ✅ Binary installed: $BINARY_PATH"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# --- Initialize config ---
|
|
55
|
+
CONFIG_PATH="$HOME/.openclaw/openclaw.json"
|
|
56
|
+
|
|
57
|
+
if [ ! -f "$CONFIG_PATH" ]; then
|
|
58
|
+
echo '{}' > "$CONFIG_PATH"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Check if channels.keychat already exists
|
|
62
|
+
if node -e "
|
|
63
|
+
const c = JSON.parse(require('fs').readFileSync('$CONFIG_PATH','utf-8'));
|
|
64
|
+
process.exit(c.channels?.keychat ? 0 : 1);
|
|
65
|
+
" 2>/dev/null; then
|
|
66
|
+
echo "[keychat] ✅ Config already has channels.keychat"
|
|
67
|
+
else
|
|
68
|
+
node -e "
|
|
69
|
+
const fs = require('fs');
|
|
70
|
+
const c = JSON.parse(fs.readFileSync('$CONFIG_PATH','utf-8'));
|
|
71
|
+
if (!c.channels) c.channels = {};
|
|
72
|
+
c.channels.keychat = { enabled: true, dmPolicy: 'open' };
|
|
73
|
+
fs.writeFileSync('$CONFIG_PATH', JSON.stringify(c, null, 2) + '\n');
|
|
74
|
+
"
|
|
75
|
+
echo "[keychat] ✅ Config initialized (channels.keychat.enabled = true, dmPolicy = open)"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
echo ""
|
|
79
|
+
echo "[keychat] Setup complete! Restart gateway to activate:"
|
|
80
|
+
echo " openclaw gateway install && openclaw gateway start"
|