@clawchatsai/connector 0.0.42 → 0.0.44

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/package.json +1 -1
  2. package/server.js +36 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawchatsai/connector",
3
- "version": "0.0.42",
3
+ "version": "0.0.44",
4
4
  "type": "module",
5
5
  "description": "ClawChats OpenClaw plugin — P2P tunnel + local API bridge",
6
6
  "main": "dist/index.js",
package/server.js CHANGED
@@ -334,14 +334,14 @@ function getActiveDb() {
334
334
  return getDb(getWorkspaces().active);
335
335
  }
336
336
 
337
- let _globalDb = null;
338
- function getGlobalDb() {
339
- if (_globalDb) return _globalDb;
340
- fs.mkdirSync(DATA_DIR, { recursive: true });
341
- const dbPath = path.join(DATA_DIR, 'global.db');
342
- _globalDb = new Database(dbPath);
343
- _globalDb.pragma('journal_mode = WAL');
344
- _globalDb.exec(`
337
+ const _globalDbCache = new Map(); // keyed by resolved dbPath
338
+ function getGlobalDb(dataDir = DATA_DIR) {
339
+ const dbPath = path.join(dataDir, 'global.db');
340
+ if (_globalDbCache.has(dbPath)) return _globalDbCache.get(dbPath);
341
+ fs.mkdirSync(dataDir, { recursive: true });
342
+ const db = new Database(dbPath);
343
+ db.pragma('journal_mode = WAL');
344
+ db.exec(`
345
345
  CREATE TABLE IF NOT EXISTS custom_emojis (
346
346
  name TEXT NOT NULL,
347
347
  pack TEXT NOT NULL DEFAULT 'slackmojis',
@@ -351,7 +351,8 @@ function getGlobalDb() {
351
351
  PRIMARY KEY (name, pack)
352
352
  )
353
353
  `);
354
- return _globalDb;
354
+ _globalDbCache.set(dbPath, db);
355
+ return db;
355
356
  }
356
357
 
357
358
  function closeDb(workspaceName) {
@@ -363,11 +364,10 @@ function closeDb(workspaceName) {
363
364
  }
364
365
 
365
366
  function closeAllDbs() {
366
- for (const [name, db] of dbCache) {
367
- db.close();
368
- }
367
+ for (const [, db] of dbCache) db.close();
369
368
  dbCache.clear();
370
- if (_globalDb) { _globalDb.close(); _globalDb = null; }
369
+ for (const [, db] of _globalDbCache) db.close();
370
+ _globalDbCache.clear();
371
371
  }
372
372
 
373
373
  function _createFtsTables(db) {
@@ -1437,7 +1437,10 @@ function handleServeFile(req, res, query) {
1437
1437
  if (!filePath) return sendError(res, 400, 'Missing path parameter');
1438
1438
 
1439
1439
  // Resolve to prevent traversal attacks
1440
- const resolved = path.resolve(filePath);
1440
+ // Relative paths (./filename) resolve against the workspace directory
1441
+ const resolved = (filePath.startsWith('./') || filePath.startsWith('../'))
1442
+ ? path.resolve(MEMORY_CONFIG.workspaceDir, filePath)
1443
+ : path.resolve(filePath);
1441
1444
 
1442
1445
  // Security: only serve files from allowed directories
1443
1446
  const allowed = ALLOWED_FILE_DIRS.some(dir => resolved.startsWith(dir + '/') || resolved === dir);
@@ -4417,7 +4420,7 @@ export function createApp(config = {}) {
4417
4420
  // Custom emoji listing (no auth)
4418
4421
  if (method === 'GET' && urlPath === '/api/emoji') {
4419
4422
  try {
4420
- const db = getGlobalDb();
4423
+ const db = getGlobalDb(_DATA_DIR);
4421
4424
  const rows = db.prepare('SELECT name, pack, url, mime_type FROM custom_emojis ORDER BY created_at DESC').all();
4422
4425
  res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=300' });
4423
4426
  return res.end(JSON.stringify(rows));
@@ -4461,7 +4464,7 @@ export function createApp(config = {}) {
4461
4464
  if (urlLower.endsWith('.gif')) mimeType = 'image/gif';
4462
4465
  else if (urlLower.endsWith('.webp')) mimeType = 'image/webp';
4463
4466
  else if (urlLower.endsWith('.jpg') || urlLower.endsWith('.jpeg')) mimeType = 'image/jpeg';
4464
- const db = getGlobalDb();
4467
+ const db = getGlobalDb(_DATA_DIR);
4465
4468
  db.prepare('INSERT OR REPLACE INTO custom_emojis (name, pack, url, mime_type) VALUES (?, ?, ?, ?)').run(safeName, targetPack, url, mimeType);
4466
4469
  res.writeHead(200, { 'Content-Type': 'application/json' });
4467
4470
  return res.end(JSON.stringify({ name: safeName, pack: targetPack, url, mime_type: mimeType }));
@@ -4473,7 +4476,7 @@ export function createApp(config = {}) {
4473
4476
  try {
4474
4477
  const { name, pack } = await parseBody(req);
4475
4478
  if (!name || !pack) { res.writeHead(400, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ error: 'Missing name or pack' })); }
4476
- const db = getGlobalDb();
4479
+ const db = getGlobalDb(_DATA_DIR);
4477
4480
  db.prepare('DELETE FROM custom_emojis WHERE name = ? AND pack = ?').run(name, pack);
4478
4481
  res.writeHead(200, { 'Content-Type': 'application/json' });
4479
4482
  return res.end(JSON.stringify({ ok: true }));
@@ -4544,6 +4547,9 @@ export function createApp(config = {}) {
4544
4547
  }
4545
4548
 
4546
4549
  // ── Browser WebSocket setup (shared logic for standalone and plugin) ────────
4550
+ // Track which sessions have already received the ClawChats file hint (once per session)
4551
+ const _clawchatsHintedSessions = new Set();
4552
+
4547
4553
  function _setupBrowserWs(wssInstance) {
4548
4554
  wssInstance.on('connection', (ws) => {
4549
4555
  console.log('Browser client connected');
@@ -4554,6 +4560,7 @@ export function createApp(config = {}) {
4554
4560
  ws.on('message', (data) => {
4555
4561
  const msgStr = data.toString();
4556
4562
  _debugLogger.logFrame('BR→SRV', msgStr);
4563
+ let forwardStr = msgStr;
4557
4564
  try {
4558
4565
  const msg = JSON.parse(msgStr);
4559
4566
  if (msg.type === 'req' && msg.method === 'connect') {
@@ -4573,8 +4580,17 @@ export function createApp(config = {}) {
4573
4580
  if (msg.action === 'debug-start') { const result = _debugLogger.start(msg.ts, ws); if (result.error === 'already-active') ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-error', error: 'Recording already active in another tab', sessionId: result.sessionId })); else ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-started', sessionId: result.sessionId })); return; }
4574
4581
  if (msg.action === 'debug-dump') { const { sessionId, files } = _debugLogger.saveDump(msg); ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-saved', sessionId, files })); return; }
4575
4582
  }
4576
- } catch { /* Not JSON or not a ClawChats message, forward to gateway */ }
4577
- _gatewayClient.sendToGateway(msgStr);
4583
+ // Inject ClawChats file hint once per session (first message only, ~15 tokens)
4584
+ if (msg.type === 'req' && msg.method === 'chat.send' && typeof msg.params?.message === 'string') {
4585
+ const sk = msg.params.sessionKey;
4586
+ if (sk && !_clawchatsHintedSessions.has(sk)) {
4587
+ _clawchatsHintedSessions.add(sk);
4588
+ msg.params.message += '\n[ClawChats context: MEDIA: tags are stripped by the gateway and will NOT display. To show images/files inline, use markdown syntax: ![alt](./filename.ext) for workspace files or ![alt](/abs/path.ext) for any path. Always use this instead of MEDIA:]';
4589
+ forwardStr = JSON.stringify(msg);
4590
+ }
4591
+ }
4592
+ } catch { /* Not JSON or not a ClawChats message, forward as-is */ }
4593
+ _gatewayClient.sendToGateway(forwardStr);
4578
4594
  });
4579
4595
 
4580
4596
  ws.on('close', () => { console.log('Browser client disconnected'); _debugLogger.handleClientDisconnect(ws); _gatewayClient.removeBrowserClient(ws); });
@@ -4630,7 +4646,7 @@ if (isDirectRun) {
4630
4646
  app.gatewayClient.connect();
4631
4647
 
4632
4648
  // Initialize global DB (custom emojis, etc.)
4633
- getGlobalDb();
4649
+ getGlobalDb(_DATA_DIR);
4634
4650
  });
4635
4651
 
4636
4652
  // Graceful shutdown