@clawchatsai/connector 0.0.39 → 0.0.41
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 +1 -1
- package/package.json +1 -1
- package/server.js +78 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @clawchatsai/connector
|
|
2
2
|
|
|
3
|
-
OpenClaw plugin for [
|
|
3
|
+
OpenClaw plugin for [ClawChats](https://clawchats.ai) — connects your local gateway to the ClawChats web app via WebRTC P2P.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -10,12 +10,50 @@ import { pipeline } from 'node:stream/promises';
|
|
|
10
10
|
import { execSync } from 'node:child_process';
|
|
11
11
|
import os from 'node:os';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
14
|
import { WebSocket as WS, WebSocketServer } from 'ws';
|
|
15
15
|
|
|
16
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
17
|
const __dirname = path.dirname(__filename);
|
|
18
18
|
|
|
19
|
+
// ─── Native Module Bootstrap (better-sqlite3) ───────────────────────────────
|
|
20
|
+
// better-sqlite3 is a native (.node) binary compiled for a specific Node.js
|
|
21
|
+
// ABI. If the installed binary doesn't match the running Node version, we
|
|
22
|
+
// auto-rebuild in-place before proceeding. Falls back to a clear error message
|
|
23
|
+
// if the user is missing build tools.
|
|
24
|
+
const _require = createRequire(import.meta.url);
|
|
25
|
+
let Database;
|
|
26
|
+
{
|
|
27
|
+
const _nativeErr = (e) =>
|
|
28
|
+
e.message && (
|
|
29
|
+
e.message.includes('did not self-register') ||
|
|
30
|
+
e.message.includes('NODE_MODULE_VERSION') ||
|
|
31
|
+
e.message.includes('was compiled against a different Node.js version')
|
|
32
|
+
);
|
|
33
|
+
try {
|
|
34
|
+
Database = _require('better-sqlite3');
|
|
35
|
+
} catch (e) {
|
|
36
|
+
if (_nativeErr(e)) {
|
|
37
|
+
console.error('[ClawChats] better-sqlite3 binary is incompatible with your Node.js version. Attempting auto-rebuild...');
|
|
38
|
+
try {
|
|
39
|
+
execSync('npm rebuild better-sqlite3', { cwd: __dirname, stdio: 'inherit' });
|
|
40
|
+
Database = _require('better-sqlite3');
|
|
41
|
+
console.log('[ClawChats] Auto-rebuild succeeded — continuing startup.');
|
|
42
|
+
} catch (rebuildErr) {
|
|
43
|
+
console.error('[ClawChats] Auto-rebuild failed. Build tools may be missing.');
|
|
44
|
+
console.error('[ClawChats] To fix, run the following, then restart the gateway:');
|
|
45
|
+
console.error(`[ClawChats] cd ${__dirname} && npm rebuild better-sqlite3`);
|
|
46
|
+
console.error('[ClawChats] If that fails, install build tools first:');
|
|
47
|
+
console.error('[ClawChats] Linux: sudo apt install build-essential python3');
|
|
48
|
+
console.error('[ClawChats] macOS: xcode-select --install');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
19
57
|
// ─── Device Identity (ed25519 signing for OpenClaw ≥2.15 scope preservation) ─
|
|
20
58
|
|
|
21
59
|
const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
|
|
@@ -1518,6 +1556,35 @@ async function handleWorkspaceFileWrite(req, res, query) {
|
|
|
1518
1556
|
send(res, 200, { ok: true });
|
|
1519
1557
|
}
|
|
1520
1558
|
|
|
1559
|
+
function handleWorkspaceFileDelete(req, res, query) {
|
|
1560
|
+
const filePath = query.path;
|
|
1561
|
+
if (!filePath) return sendError(res, 400, 'Missing path parameter');
|
|
1562
|
+
|
|
1563
|
+
const resolved = path.resolve(filePath.replace(/^~/, HOME));
|
|
1564
|
+
|
|
1565
|
+
// Security: only allow paths under home directory
|
|
1566
|
+
if (!resolved.startsWith(HOME)) {
|
|
1567
|
+
return sendError(res, 403, 'Access denied');
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
if (!fs.existsSync(resolved)) {
|
|
1571
|
+
return sendError(res, 404, 'Path not found');
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
try {
|
|
1575
|
+
const stat = fs.statSync(resolved);
|
|
1576
|
+
if (stat.isDirectory()) {
|
|
1577
|
+
fs.rmSync(resolved, { recursive: true, force: true });
|
|
1578
|
+
send(res, 200, { ok: true, type: 'dir' });
|
|
1579
|
+
} else {
|
|
1580
|
+
fs.unlinkSync(resolved);
|
|
1581
|
+
send(res, 200, { ok: true, type: 'file' });
|
|
1582
|
+
}
|
|
1583
|
+
} catch (err) {
|
|
1584
|
+
sendError(res, 500, 'Delete failed: ' + err.message);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1521
1588
|
async function handleWorkspaceUpload(req, res, query) {
|
|
1522
1589
|
const targetDir = query.path;
|
|
1523
1590
|
if (!targetDir) return sendError(res, 400, 'Missing path parameter');
|
|
@@ -2250,6 +2317,9 @@ async function handleRequest(req, res) {
|
|
|
2250
2317
|
if (method === 'PUT' && urlPath === '/api/workspace/file') {
|
|
2251
2318
|
return await handleWorkspaceFileWrite(req, res, query);
|
|
2252
2319
|
}
|
|
2320
|
+
if (method === 'DELETE' && urlPath === '/api/workspace/file') {
|
|
2321
|
+
return handleWorkspaceFileDelete(req, res, query);
|
|
2322
|
+
}
|
|
2253
2323
|
if (method === 'POST' && urlPath === '/api/workspace/upload') {
|
|
2254
2324
|
return await handleWorkspaceUpload(req, res, query);
|
|
2255
2325
|
}
|
|
@@ -2663,11 +2733,9 @@ class GatewayClient {
|
|
|
2663
2733
|
// Auto-generate AI title upgrade after first assistant response
|
|
2664
2734
|
// Heuristic was already set on user message save; this fires the AI upgrade
|
|
2665
2735
|
const currentTitle = db.prepare('SELECT title FROM threads WHERE id = ?').get(parsed.threadId)?.title;
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
this.generateThreadTitle(db, parsed.threadId, parsed.workspace, true);
|
|
2670
|
-
}
|
|
2736
|
+
const msgCount = db.prepare('SELECT COUNT(*) as c FROM messages WHERE thread_id = ?').get(parsed.threadId).c;
|
|
2737
|
+
if (msgCount === 2 || currentTitle === 'New chat') {
|
|
2738
|
+
this.generateThreadTitle(db, parsed.threadId, parsed.workspace, true);
|
|
2671
2739
|
}
|
|
2672
2740
|
} catch (e) {
|
|
2673
2741
|
console.error(`Failed to save assistant message:`, e.message);
|
|
@@ -3987,11 +4055,9 @@ export function createApp(config = {}) {
|
|
|
3987
4055
|
|
|
3988
4056
|
// Auto-generate AI title upgrade after first assistant response
|
|
3989
4057
|
const currentTitle = db.prepare('SELECT title FROM threads WHERE id = ?').get(parsed.threadId)?.title;
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
this.generateThreadTitle(db, parsed.threadId, parsed.workspace, true);
|
|
3994
|
-
}
|
|
4058
|
+
const msgCount = db.prepare('SELECT COUNT(*) as c FROM messages WHERE thread_id = ?').get(parsed.threadId).c;
|
|
4059
|
+
if (msgCount === 2 || currentTitle === 'New chat') {
|
|
4060
|
+
this.generateThreadTitle(db, parsed.threadId, parsed.workspace, true);
|
|
3995
4061
|
}
|
|
3996
4062
|
} catch (e) { console.error(`Failed to save assistant message:`, e.message); }
|
|
3997
4063
|
}
|
|
@@ -4370,6 +4436,7 @@ export function createApp(config = {}) {
|
|
|
4370
4436
|
if (method === 'GET' && urlPath === '/api/workspace') return handleWorkspaceList(req, res, query);
|
|
4371
4437
|
if (method === 'GET' && urlPath === '/api/workspace/file') return handleWorkspaceFileRead(req, res, query);
|
|
4372
4438
|
if (method === 'PUT' && urlPath === '/api/workspace/file') return await handleWorkspaceFileWrite(req, res, query);
|
|
4439
|
+
if (method === 'DELETE' && urlPath === '/api/workspace/file') return handleWorkspaceFileDelete(req, res, query);
|
|
4373
4440
|
if (method === 'POST' && urlPath === '/api/workspace/upload') return await handleWorkspaceUpload(req, res, query);
|
|
4374
4441
|
if (method === 'GET' && urlPath === '/api/memory/status') return await _handleMemoryStatus(req, res);
|
|
4375
4442
|
if (method === 'GET' && urlPath === '/api/memory/list') return await _handleMemoryList(req, res, query);
|