@badgerclaw/connect 1.3.3 → 1.4.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@badgerclaw/connect",
3
- "version": "1.3.3",
3
+ "version": "1.4.0",
4
4
  "description": "BadgerClaw channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -30,5 +30,8 @@
30
30
  "localPath": "extensions/badgerclaw",
31
31
  "defaultChoice": "npm"
32
32
  }
33
+ },
34
+ "scripts": {
35
+ "postinstall": "node scripts/postinstall.js"
33
36
  }
34
37
  }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postinstall.js — patches openclaw package.json exports map to expose
4
+ * plugin-sdk/compat subpath, which was removed in openclaw >=2026.3.24.
5
+ * This runs automatically after `npm install` in the plugin directory.
6
+ */
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ const OPENCLAW_PKG = path.join(
11
+ path.dirname(require.resolve('openclaw/package.json')),
12
+ 'package.json'
13
+ );
14
+
15
+ const COMPAT_EXPORT = './plugin-sdk/compat';
16
+ const COMPAT_ENTRY = {
17
+ types: './dist/plugin-sdk/compat.d.ts',
18
+ default: './dist/plugin-sdk/compat.js',
19
+ };
20
+
21
+ try {
22
+ const pkg = JSON.parse(fs.readFileSync(OPENCLAW_PKG, 'utf-8'));
23
+ if (!pkg.exports) pkg.exports = {};
24
+ if (!pkg.exports[COMPAT_EXPORT]) {
25
+ pkg.exports[COMPAT_EXPORT] = COMPAT_ENTRY;
26
+ fs.writeFileSync(OPENCLAW_PKG, JSON.stringify(pkg, null, 2));
27
+ console.log('[badgerclaw] patched openclaw exports map: added plugin-sdk/compat');
28
+ } else {
29
+ console.log('[badgerclaw] openclaw exports map already has plugin-sdk/compat');
30
+ }
31
+ } catch (e) {
32
+ // Non-fatal — openclaw may not be installed yet
33
+ console.log('[badgerclaw] postinstall: could not patch openclaw exports map:', e.message);
34
+ }
@@ -342,6 +342,80 @@ export async function handleBotCommand(params: {
342
342
  return true;
343
343
  }
344
344
 
345
+ case "delete": {
346
+ const nameArg = parts.slice(2).join(" ").trim().replace(/^@/, "").split(":")[0].replace(/_bot$/i, "").toLowerCase();
347
+ if (!nameArg) {
348
+ await client.sendMessage(roomId, {
349
+ msgtype: "m.text",
350
+ body: "Usage: /bot delete <name>\nExample: /bot delete jarvis\n\n⚠️ This permanently deletes the bot from the database and Matrix.",
351
+ });
352
+ return true;
353
+ }
354
+
355
+ const botUserId = `@${nameArg}_bot:badger.signout.io`;
356
+
357
+ // Load JWT token from ~/.badgerclaw/auth.json
358
+ let apiToken: string | null = null;
359
+ try {
360
+ const authPath = require("path").join(process.env.HOME || "/tmp", ".badgerclaw", "auth.json");
361
+ const authData = JSON.parse(require("fs").readFileSync(authPath, "utf-8"));
362
+ apiToken = authData.access_token || null;
363
+ } catch {
364
+ // no token
365
+ }
366
+
367
+ if (!apiToken) {
368
+ await client.sendMessage(roomId, {
369
+ msgtype: "m.text",
370
+ body: "❌ Not authenticated. Run `badgerclaw login` first.",
371
+ });
372
+ return true;
373
+ }
374
+
375
+ // Find the bot by user_id via API
376
+ try {
377
+ const listResp = await fetch("https://api.badgerclaw.ai/api/v1/bots", {
378
+ headers: { Authorization: `Bearer ${apiToken}`, "Content-Type": "application/json" },
379
+ });
380
+ if (!listResp.ok) throw new Error(`Failed to list bots: ${listResp.status}`);
381
+ const bots: Array<{ id: string; bot_user_id: string; bot_name: string }> = await listResp.json();
382
+ const bot = bots.find((b) => b.bot_user_id.toLowerCase() === botUserId.toLowerCase());
383
+
384
+ if (!bot) {
385
+ await client.sendMessage(roomId, {
386
+ msgtype: "m.text",
387
+ body: `❌ Bot @${nameArg}_bot not found. Check the name and try again.`,
388
+ });
389
+ return true;
390
+ }
391
+
392
+ // Delete permanently
393
+ const delResp = await fetch(`https://api.badgerclaw.ai/api/v1/bots/${bot.id}`, {
394
+ method: "DELETE",
395
+ headers: { Authorization: `Bearer ${apiToken}`, "Content-Type": "application/json" },
396
+ });
397
+
398
+ if (delResp.ok) {
399
+ await client.sendMessage(roomId, {
400
+ msgtype: "m.text",
401
+ body: `🗑️ Bot **${bot.bot_name}** (@${nameArg}_bot) permanently deleted.`,
402
+ });
403
+ } else {
404
+ const err = await delResp.json().catch(() => ({ detail: delResp.statusText }));
405
+ await client.sendMessage(roomId, {
406
+ msgtype: "m.text",
407
+ body: `❌ Failed to delete bot: ${err.detail || delResp.status}`,
408
+ });
409
+ }
410
+ } catch (e) {
411
+ await client.sendMessage(roomId, {
412
+ msgtype: "m.text",
413
+ body: `❌ Error deleting bot: ${e}`,
414
+ });
415
+ }
416
+ return true;
417
+ }
418
+
345
419
  default: {
346
420
  await client.sendMessage(roomId, {
347
421
  msgtype: "m.text",
@@ -164,5 +164,87 @@ export function registerMatrixMonitorEvents(params: {
164
164
  `badgerclaw: member event room=${roomId} stateKey=${stateKey} membership=${membership ?? "unknown"}`,
165
165
  );
166
166
  }
167
+
168
+ // Auto-pair: iOS app sends this event after generating a pair code
169
+ // The plugin redeems the code and adds the bot account to OpenClaw config
170
+ if (eventType === "com.badgerclaw.autopair") {
171
+ const content = event?.content as {
172
+ pair_code?: string;
173
+ bot_name?: string;
174
+ bot_user_id?: string;
175
+ owner_matrix_id?: string;
176
+ } | undefined;
177
+
178
+ const pairCode = content?.pair_code;
179
+ const botName = content?.bot_name;
180
+ const botUserId = content?.bot_user_id;
181
+
182
+ if (!pairCode || !botUserId) {
183
+ logVerboseMessage(`badgerclaw: autopair event missing fields room=${roomId}`);
184
+ return;
185
+ }
186
+
187
+ logger.info(`badgerclaw: autopair event received — redeeming code for ${botUserId}`, { roomId });
188
+
189
+ void (async () => {
190
+ try {
191
+ const resp = await fetch("https://api.badgerclaw.ai/api/v1/pairing/redeem", {
192
+ method: "POST",
193
+ headers: { "Content-Type": "application/json" },
194
+ body: JSON.stringify({ code: pairCode }),
195
+ });
196
+
197
+ if (!resp.ok) {
198
+ const err = await resp.json().catch(() => ({ detail: resp.statusText }));
199
+ logger.warn(`badgerclaw: autopair redeem failed for ${botUserId}: ${err.detail || resp.status}`);
200
+ return;
201
+ }
202
+
203
+ const data = await resp.json() as {
204
+ homeserver: string;
205
+ access_token: string;
206
+ user_id: string;
207
+ bot_name: string;
208
+ device_id: string;
209
+ };
210
+
211
+ // Write new bot account to OpenClaw config
212
+ const fs = await import("fs");
213
+ const configPath = require("path").join(process.env.HOME || "/tmp", ".openclaw", "openclaw.json");
214
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
215
+
216
+ if (!config.channels) config.channels = {};
217
+ if (!config.channels.badgerclaw) config.channels.badgerclaw = {};
218
+ if (!config.channels.badgerclaw.accounts) config.channels.badgerclaw.accounts = {};
219
+
220
+ // Derive account key from bot localpart (e.g. @think_bot:... → "think")
221
+ const localpart = data.user_id.split(":")[0].replace("@", "").replace(/_bot$/, "");
222
+
223
+ config.channels.badgerclaw.accounts[localpart] = {
224
+ userId: data.user_id,
225
+ accessToken: data.access_token,
226
+ homeserver: data.homeserver,
227
+ encryption: true,
228
+ deviceId: data.device_id,
229
+ };
230
+
231
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
232
+
233
+ logger.info(`badgerclaw: ✅ autopair complete — added account "${localpart}" (${data.user_id}) to OpenClaw config. Restart gateway to activate.`);
234
+
235
+ // Notify in the room
236
+ try {
237
+ await client.sendMessage(roomId, {
238
+ msgtype: "m.text",
239
+ body: `✅ Bot **${data.bot_name}** (${data.user_id}) has been automatically paired and added to your OpenClaw instance.\n\nRestart your OpenClaw gateway to activate: \`openclaw gateway restart\``,
240
+ });
241
+ } catch {
242
+ // non-fatal
243
+ }
244
+ } catch (e) {
245
+ logger.error(`badgerclaw: autopair failed for ${botUserId}: ${String(e)}`);
246
+ }
247
+ })();
248
+ }
167
249
  });
168
250
  }