clawhq-bridge 1.0.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/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # ClawHQ Bridge
2
+
3
+ Connect your local [OpenClaw](https://docs.openclaw.ai) agent to [ClawHQ](https://clawhq.dev) — chat with your AI from the ClawHQ iOS app or web dashboard.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx clawhq-bridge --setup YOUR_SETUP_CODE
9
+ ```
10
+
11
+ Get your setup code from **Agent Settings** in the ClawHQ app after creating a Connector agent.
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ # First-time setup (auto-detects OpenClaw)
17
+ npx clawhq-bridge --setup <SETUP_CODE>
18
+
19
+ # Run bridge (after setup)
20
+ npx clawhq-bridge
21
+
22
+ # Check status
23
+ npx clawhq-bridge --status
24
+ ```
25
+
26
+ ## How It Works
27
+
28
+ 1. You create a **Connector** agent in ClawHQ
29
+ 2. Run the setup command — it exchanges your code for credentials and detects your OpenClaw installation
30
+ 3. The bridge connects to ClawHQ via Socket.io and forwards messages to your local OpenClaw gateway
31
+ 4. Chat with your agent from anywhere — iOS, web, or desktop
32
+
33
+ ## Requirements
34
+
35
+ - [Node.js](https://nodejs.org) 16+
36
+ - [OpenClaw](https://docs.openclaw.ai) running locally
37
+
38
+ ## Options
39
+
40
+ | Flag | Description |
41
+ |------|-------------|
42
+ | `--setup <code>` | First-time setup with code from ClawHQ |
43
+ | `--session <key>` | OpenClaw session key (default: `clawhq:app`) |
44
+ | `--port <port>` | OpenClaw gateway port (default: `18789`) |
45
+ | `--status` | Show bridge config and status |
46
+ | `--help` | Show help |
47
+
48
+ ## Links
49
+
50
+ - [ClawHQ](https://clawhq.dev) — AI Agent Platform
51
+ - [OpenClaw](https://docs.openclaw.ai) — Open-source AI agent framework
52
+ - [Support](https://clawhq.dev/support)
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const https = require('https');
7
+ const http = require('http');
8
+ const { execSync } = require('child_process');
9
+
10
+ const CLAWHQ_API = 'https://clawhq-api-production-f6d7.up.railway.app';
11
+ const BRIDGE_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', 'ClawHQ', 'bridge');
12
+
13
+ // ─── Parse CLI args ───
14
+ const args = process.argv.slice(2);
15
+ const flags = {};
16
+ for (let i = 0; i < args.length; i++) {
17
+ if (args[i].startsWith('--')) {
18
+ const key = args[i].replace(/^--/, '');
19
+ flags[key] = args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true;
20
+ }
21
+ }
22
+
23
+ if (flags.help || args.includes('-h')) {
24
+ console.log(`
25
+ ClawHQ Bridge — Connect your local OpenClaw to ClawHQ
26
+
27
+ Usage:
28
+ npx clawhq-bridge --setup <SETUP_CODE> First-time setup + run
29
+ npx clawhq-bridge Run with existing config
30
+ npx clawhq-bridge --status Check bridge status
31
+
32
+ Options:
33
+ --setup <code> Setup code from ClawHQ agent settings
34
+ --session <key> OpenClaw session key (default: clawhq:app)
35
+ --port <port> OpenClaw gateway port (default: 18789)
36
+ --status Check if bridge is configured
37
+ --help, -h Show this help
38
+ `);
39
+ process.exit(0);
40
+ }
41
+
42
+ // ─── Setup mode ───
43
+ if (flags.setup) {
44
+ setup(flags.setup).catch(err => {
45
+ console.error(`\n❌ Setup failed: ${err.message}`);
46
+ process.exit(1);
47
+ });
48
+ } else if (flags.status) {
49
+ showStatus();
50
+ } else {
51
+ // Run mode — load config and start
52
+ run().catch(err => {
53
+ console.error(`\n❌ ${err.message}`);
54
+ process.exit(1);
55
+ });
56
+ }
57
+
58
+ // ═══════════════════════════════════════════════════════════
59
+ // Setup — Exchange code, detect OpenClaw, write config, run
60
+ // ═══════════════════════════════════════════════════════════
61
+
62
+ async function setup(setupCode) {
63
+ console.log('');
64
+ console.log('╔══════════════════════════════════════════════════╗');
65
+ console.log('║ 🦞 ClawHQ Bridge — Setup ║');
66
+ console.log('╚══════════════════════════════════════════════════╝');
67
+ console.log('');
68
+
69
+ // 1. Exchange setup code
70
+ console.log('⏳ Exchanging setup code...');
71
+ const creds = await exchangeSetupCode(setupCode);
72
+ console.log(`✅ Agent: ${creds.agentName} (${creds.agentId})`);
73
+
74
+ // 2. Detect OpenClaw
75
+ console.log('');
76
+ console.log('⏳ Detecting OpenClaw gateway...');
77
+ const ocConfig = detectOpenClaw();
78
+ if (ocConfig) {
79
+ console.log(`✅ Found OpenClaw at port ${ocConfig.port}`);
80
+ console.log(` Token: ${ocConfig.token.substring(0, 8)}...`);
81
+ } else {
82
+ console.log('⚠️ OpenClaw not detected. Install it from https://docs.openclaw.ai');
83
+ console.log(' You can still configure the bridge and start OpenClaw later.');
84
+ }
85
+
86
+ // 3. Write config
87
+ console.log('');
88
+ console.log(`⏳ Writing config to ${BRIDGE_DIR}/.env`);
89
+ fs.mkdirSync(BRIDGE_DIR, { recursive: true });
90
+
91
+ const sessionKey = flags.session || 'clawhq:app';
92
+ const port = flags.port || (ocConfig ? ocConfig.port : 18789);
93
+
94
+ const envContent = [
95
+ `# ClawHQ Bridge Config — Auto-generated`,
96
+ `# Agent: ${creds.agentName}`,
97
+ `CLAWHQ_URL=${CLAWHQ_API}`,
98
+ `ACCESS_TOKEN=${creds.accessToken}`,
99
+ `AGENT_ID=${creds.agentId}`,
100
+ `OPENCLAW_PORT=${port}`,
101
+ `OPENCLAW_TOKEN=${ocConfig ? ocConfig.token : 'PASTE_YOUR_TOKEN_HERE'}`,
102
+ `OPENCLAW_SESSION_KEY=${sessionKey}`,
103
+ ].join('\n');
104
+
105
+ fs.writeFileSync(path.join(BRIDGE_DIR, '.env'), envContent);
106
+ console.log('✅ Config saved');
107
+
108
+ // 4. Start bridge
109
+ console.log('');
110
+ console.log('🚀 Starting bridge...');
111
+ console.log('');
112
+ await startBridge();
113
+ }
114
+
115
+ // ═══════════════════════════════════════════════════════════
116
+ // Run — Load config and start bridge
117
+ // ═══════════════════════════════════════════════════════════
118
+
119
+ async function run() {
120
+ const envPath = path.join(BRIDGE_DIR, '.env');
121
+ if (!fs.existsSync(envPath)) {
122
+ throw new Error(
123
+ 'Bridge not configured. Run: npx clawhq-bridge --setup <SETUP_CODE>\n' +
124
+ 'Get your setup code from Agent Settings in ClawHQ.'
125
+ );
126
+ }
127
+ loadEnv(envPath);
128
+ await startBridge();
129
+ }
130
+
131
+ // ═══════════════════════════════════════════════════════════
132
+ // Bridge Core — Socket.io ↔ OpenClaw
133
+ // ═══════════════════════════════════════════════════════════
134
+
135
+ async function startBridge() {
136
+ const CLAWHQ_URL = process.env.CLAWHQ_URL || CLAWHQ_API;
137
+ const ACCESS_TOKEN = process.env.ACCESS_TOKEN || '';
138
+ const AGENT_ID = process.env.AGENT_ID || '';
139
+ const OPENCLAW_PORT = parseInt(process.env.OPENCLAW_PORT || '18789', 10);
140
+ const OPENCLAW_TOKEN = process.env.OPENCLAW_TOKEN || '';
141
+ const SESSION_KEY = process.env.OPENCLAW_SESSION_KEY || 'clawhq:app';
142
+
143
+ if (!ACCESS_TOKEN || !AGENT_ID) {
144
+ throw new Error('Missing credentials. Re-run setup: npx clawhq-bridge --setup <CODE>');
145
+ }
146
+
147
+ console.log('[ClawHQ Bridge] Starting...');
148
+ console.log(` ClawHQ: ${CLAWHQ_URL}`);
149
+ console.log(` Agent: ${AGENT_ID}`);
150
+ console.log(` OpenClaw: http://127.0.0.1:${OPENCLAW_PORT}`);
151
+ console.log(` Session: ${SESSION_KEY}`);
152
+ console.log('');
153
+
154
+ const ioClient = require('socket.io-client');
155
+
156
+ function sendToOpenClaw(message) {
157
+ return new Promise((resolve, reject) => {
158
+ const body = JSON.stringify({
159
+ model: 'openclaw',
160
+ messages: [{ role: 'user', content: message }],
161
+ stream: false,
162
+ });
163
+
164
+ const req = http.request({
165
+ hostname: '127.0.0.1',
166
+ port: OPENCLAW_PORT,
167
+ path: '/v1/chat/completions',
168
+ method: 'POST',
169
+ headers: {
170
+ 'Content-Type': 'application/json',
171
+ 'Authorization': `Bearer ${OPENCLAW_TOKEN}`,
172
+ 'x-openclaw-session-key': SESSION_KEY,
173
+ 'Content-Length': Buffer.byteLength(body),
174
+ },
175
+ timeout: 300000,
176
+ }, (res) => {
177
+ let data = '';
178
+ res.on('data', (chunk) => data += chunk);
179
+ res.on('end', () => {
180
+ if (res.statusCode !== 200) {
181
+ reject(new Error(`OpenClaw ${res.statusCode}: ${data.substring(0, 200)}`));
182
+ return;
183
+ }
184
+ try {
185
+ const json = JSON.parse(data);
186
+ const content = json.choices?.[0]?.message?.content || 'No response';
187
+ resolve(content);
188
+ } catch (e) {
189
+ reject(new Error(`Parse error: ${data.substring(0, 200)}`));
190
+ }
191
+ });
192
+ });
193
+
194
+ req.on('timeout', () => { req.destroy(); reject(new Error('OpenClaw timeout (5 min)')); });
195
+ req.on('error', (err) => {
196
+ if (err.code === 'ECONNREFUSED') {
197
+ reject(new Error(`OpenClaw not running on port ${OPENCLAW_PORT}. Start it with: openclaw gateway start`));
198
+ } else {
199
+ reject(err);
200
+ }
201
+ });
202
+ req.write(body);
203
+ req.end();
204
+ });
205
+ }
206
+
207
+ const socket = ioClient(CLAWHQ_URL, {
208
+ auth: { token: ACCESS_TOKEN },
209
+ reconnection: true,
210
+ reconnectionDelay: 5000,
211
+ reconnectionAttempts: Infinity,
212
+ transports: ['polling', 'websocket'],
213
+ });
214
+
215
+ socket.on('connect', () => {
216
+ console.log(`[ClawHQ] Connected (${socket.id})`);
217
+ socket.emit('bridge:register', { agentId: AGENT_ID });
218
+ });
219
+
220
+ socket.on('bridge:registered', () => {
221
+ console.log('[ClawHQ] ✅ Bridge registered');
222
+ console.log('');
223
+ console.log('╔══════════════════════════════════════════════════╗');
224
+ console.log('║ ✅ Bridge is running! ║');
225
+ console.log('║ Chat with your agent in the ClawHQ app now. ║');
226
+ console.log('║ ║');
227
+ console.log('║ Press Ctrl+C to stop. ║');
228
+ console.log('╚══════════════════════════════════════════════════╝');
229
+ console.log('');
230
+ });
231
+
232
+ socket.on('bridge:message', async (data) => {
233
+ const userMessage = data.content || '';
234
+ console.log(`[Bridge] → "${userMessage.substring(0, 60)}"`);
235
+
236
+ try {
237
+ const response = await sendToOpenClaw(userMessage);
238
+ console.log(`[Bridge] ← "${response.substring(0, 80)}"`);
239
+ socket.emit('bridge:response', {
240
+ agentId: AGENT_ID,
241
+ messageId: data.messageId,
242
+ content: response,
243
+ });
244
+ } catch (err) {
245
+ console.error(`[Bridge] Error:`, err.message);
246
+ socket.emit('bridge:response', {
247
+ agentId: AGENT_ID,
248
+ messageId: data.messageId,
249
+ content: `❌ ${err.message}`,
250
+ });
251
+ }
252
+ });
253
+
254
+ socket.on('bridge:error', (data) => console.error(`[ClawHQ] Error: ${data.message}`));
255
+ socket.on('disconnect', (reason) => console.log(`[ClawHQ] Disconnected: ${reason}`));
256
+ socket.on('connect_error', (err) => console.error(`[ClawHQ] Connection error: ${err.message}`));
257
+
258
+ // Keep alive
259
+ process.on('SIGINT', () => {
260
+ console.log('\n[Bridge] Shutting down...');
261
+ socket.disconnect();
262
+ process.exit(0);
263
+ });
264
+ }
265
+
266
+ // ═══════════════════════════════════════════════════════════
267
+ // Helpers
268
+ // ═══════════════════════════════════════════════════════════
269
+
270
+ function exchangeSetupCode(code) {
271
+ return new Promise((resolve, reject) => {
272
+ const body = JSON.stringify({ setupCode: code });
273
+ const url = new URL(`${CLAWHQ_API}/api/agents/bridge/exchange`);
274
+
275
+ const req = https.request({
276
+ hostname: url.hostname,
277
+ port: 443,
278
+ path: url.pathname,
279
+ method: 'POST',
280
+ headers: {
281
+ 'Content-Type': 'application/json',
282
+ 'Content-Length': Buffer.byteLength(body),
283
+ },
284
+ }, (res) => {
285
+ let data = '';
286
+ res.on('data', (chunk) => data += chunk);
287
+ res.on('end', () => {
288
+ if (res.statusCode !== 200) {
289
+ try {
290
+ const err = JSON.parse(data);
291
+ reject(new Error(err.error || `HTTP ${res.statusCode}`));
292
+ } catch {
293
+ reject(new Error(`HTTP ${res.statusCode}: ${data.substring(0, 200)}`));
294
+ }
295
+ return;
296
+ }
297
+ try {
298
+ resolve(JSON.parse(data));
299
+ } catch {
300
+ reject(new Error('Invalid response from server'));
301
+ }
302
+ });
303
+ });
304
+
305
+ req.on('error', reject);
306
+ req.write(body);
307
+ req.end();
308
+ });
309
+ }
310
+
311
+ function detectOpenClaw() {
312
+ try {
313
+ const configPaths = [
314
+ path.join(process.env.HOME || '', '.openclaw', 'openclaw.json'),
315
+ path.join(process.env.USERPROFILE || '', '.openclaw', 'openclaw.json'),
316
+ ];
317
+
318
+ for (const p of configPaths) {
319
+ if (fs.existsSync(p)) {
320
+ const config = JSON.parse(fs.readFileSync(p, 'utf-8'));
321
+ const port = config.gateway?.port || 18789;
322
+ const token = config.gateway?.auth?.token || config.gateway?.token || '';
323
+ if (token) return { port, token };
324
+ }
325
+ }
326
+ } catch {}
327
+ return null;
328
+ }
329
+
330
+ function loadEnv(envPath) {
331
+ fs.readFileSync(envPath, 'utf-8').split('\n').forEach(line => {
332
+ const trimmed = line.trim();
333
+ if (!trimmed || trimmed.startsWith('#')) return;
334
+ const eqIdx = trimmed.indexOf('=');
335
+ if (eqIdx === -1) return;
336
+ const key = trimmed.substring(0, eqIdx).trim();
337
+ const val = trimmed.substring(eqIdx + 1).trim();
338
+ if (key) process.env[key] = val;
339
+ });
340
+ }
341
+
342
+ function showStatus() {
343
+ const envPath = path.join(BRIDGE_DIR, '.env');
344
+ if (!fs.existsSync(envPath)) {
345
+ console.log('❌ Bridge not configured.');
346
+ console.log(' Run: npx clawhq-bridge --setup <SETUP_CODE>');
347
+ process.exit(1);
348
+ }
349
+ loadEnv(envPath);
350
+ console.log('');
351
+ console.log('ClawHQ Bridge Status');
352
+ console.log('────────────────────');
353
+ console.log(` Config: ${envPath}`);
354
+ console.log(` Agent: ${process.env.AGENT_ID || 'not set'}`);
355
+ console.log(` Session: ${process.env.OPENCLAW_SESSION_KEY || 'clawhq:app'}`);
356
+ console.log(` OC Port: ${process.env.OPENCLAW_PORT || '18789'}`);
357
+ console.log(` OC Token: ${(process.env.OPENCLAW_TOKEN || '').substring(0, 8)}...`);
358
+ console.log('');
359
+
360
+ // Check OpenClaw
361
+ const oc = detectOpenClaw();
362
+ console.log(` OpenClaw: ${oc ? `✅ Detected (port ${oc.port})` : '❌ Not found'}`);
363
+ console.log('');
364
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "clawhq-bridge",
3
+ "version": "1.0.0",
4
+ "description": "ClawHQ Bridge — Connect your local OpenClaw agent to ClawHQ",
5
+ "bin": {
6
+ "clawhq-bridge": "./bin/clawhq-bridge.js"
7
+ },
8
+ "keywords": ["clawhq", "openclaw", "ai", "agent", "bridge"],
9
+ "author": "ClawHQ <support@clawhq.dev>",
10
+ "license": "MIT",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/Malchiior/clawhq"
14
+ },
15
+ "homepage": "https://clawhq.dev",
16
+ "engines": {
17
+ "node": ">=16"
18
+ },
19
+ "dependencies": {
20
+ "socket.io-client": "^4.7.0"
21
+ }
22
+ }