@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
|
+
"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
|
}
|