@badgerclaw/connect 1.4.7 → 1.4.9

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.
Files changed (2) hide show
  1. package/index.ts +91 -0
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -4,6 +4,95 @@ import { matrixPlugin } from "./src/channel.js";
4
4
  import { ensureMatrixCryptoRuntime } from "./src/matrix/deps.js";
5
5
  import { setMatrixRuntime } from "./src/runtime.js";
6
6
 
7
+ let _pairWatcherStarted = false;
8
+
9
+ async function startPairWatcher(logger: { info?: (msg: string) => void; warn?: (msg: string) => void }): Promise<void> {
10
+ if (_pairWatcherStarted) return;
11
+ _pairWatcherStarted = true;
12
+ const log = logger.info ?? console.log;
13
+ const warn = logger.warn ?? console.warn;
14
+
15
+ // Load JWT from ~/.badgerclaw/auth.json — written by `badgerclaw login`
16
+ let token: string | null = null;
17
+ try {
18
+ const fs = await import("fs");
19
+ const path = await import("path");
20
+ const os = await import("os");
21
+ const authPath = path.join(os.homedir(), ".badgerclaw", "auth.json");
22
+ if (fs.existsSync(authPath)) {
23
+ const auth = JSON.parse(fs.readFileSync(authPath, "utf-8"));
24
+ token = auth.access_token ?? null;
25
+ }
26
+ } catch {}
27
+
28
+ if (!token) {
29
+ log("badgerclaw: no CLI auth found — run `badgerclaw login` to enable auto-pair");
30
+ return;
31
+ }
32
+
33
+ const API_BASE = "https://api.badgerclaw.ai";
34
+ log("badgerclaw: pair-watcher started — listening for pair events from iOS app");
35
+
36
+ const connect = () => {
37
+ const EventSource = require("/opt/homebrew/lib/node_modules/badgerclaw/node_modules/eventsource");
38
+ const es = new EventSource(`${API_BASE}/api/v1/openclaw/events`, {
39
+ headers: { Authorization: `Bearer ${token}` },
40
+ });
41
+
42
+ es.onmessage = async (event: any) => {
43
+ try {
44
+ const data = JSON.parse(event.data);
45
+ if (data.type !== "pair") return;
46
+
47
+ log(`badgerclaw: pair event received for ${data.bot_user_id}`);
48
+
49
+ // Redeem the code
50
+ const redeemResp = await fetch(`${API_BASE}/api/v1/pairing/redeem`, {
51
+ method: "POST",
52
+ headers: { "Content-Type": "application/json" },
53
+ body: JSON.stringify({ code: data.pair_code }),
54
+ });
55
+
56
+ if (!redeemResp.ok) {
57
+ warn(`badgerclaw: pair redeem failed: ${redeemResp.status}`);
58
+ return;
59
+ }
60
+
61
+ const bot = await redeemResp.json() as {
62
+ homeserver: string; access_token: string; user_id: string; bot_name: string; device_id: string;
63
+ };
64
+
65
+ // Write to config via openclaw config set — triggers hot-reload, bot starts immediately
66
+ const { execSync } = await import("child_process");
67
+ const localpart = bot.user_id.split(":")[0].replace("@", "").replace(/_bot$/, "");
68
+ const base = `channels.badgerclaw.accounts.${localpart}`;
69
+ const opts = { encoding: "utf-8" as const, timeout: 10000, stdio: "pipe" as const };
70
+ execSync(`openclaw config set ${base}.userId "${bot.user_id}"`, opts);
71
+ execSync(`openclaw config set ${base}.accessToken "${bot.access_token}"`, opts);
72
+ execSync(`openclaw config set ${base}.homeserver "${bot.homeserver}"`, opts);
73
+ execSync(`openclaw config set ${base}.encryption true`, opts);
74
+ if (bot.device_id) execSync(`openclaw config set ${base}.deviceId "${bot.device_id}"`, opts);
75
+
76
+ // Mark claimed
77
+ await fetch(`${API_BASE}/api/v1/openclaw/pending-pairs/${data.pair_code}/claim`, {
78
+ method: "POST",
79
+ headers: { Authorization: `Bearer ${token}` },
80
+ }).catch(() => {});
81
+
82
+ log(`badgerclaw: ✅ ${bot.bot_name} (${bot.user_id}) auto-paired and live`);
83
+ } catch (e) {
84
+ warn(`badgerclaw: pair event error: ${String(e)}`);
85
+ }
86
+ };
87
+
88
+ es.onerror = () => {
89
+ // EventSource auto-reconnects — silent
90
+ };
91
+ };
92
+
93
+ connect();
94
+ }
95
+
7
96
  async function checkPluginUpdate(log: (msg: string) => void): Promise<void> {
8
97
  try {
9
98
  const CURRENT = "1.4.4";
@@ -33,6 +122,8 @@ const plugin = {
33
122
  register(api: OpenClawPluginApi) {
34
123
  setMatrixRuntime(api.runtime);
35
124
  void checkPluginUpdate(api.logger.info ?? console.log);
125
+ // Start SSE pair-watcher — no external process needed, runs inside OpenClaw gateway
126
+ void startPairWatcher(api.logger).catch(() => {});
36
127
  void ensureMatrixCryptoRuntime({ log: api.logger.info }).catch((err) => {
37
128
  const message = err instanceof Error ? err.message : String(err);
38
129
  api.logger.warn?.(`badgerclaw: crypto runtime bootstrap failed: ${message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@badgerclaw/connect",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "BadgerClaw channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "dependencies": {