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 +52 -0
- package/bin/clawhq-bridge.js +364 -0
- package/package.json +22 -0
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
|
+
}
|