@clawchatsai/connector 0.0.10 → 0.0.11

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 +4 -2
  2. package/server.js +29 -41
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawchatsai/connector",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "type": "module",
5
5
  "description": "ShellChat OpenClaw plugin — P2P tunnel + local API bridge",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,9 @@
29
29
  "ws": "^8.0.0"
30
30
  },
31
31
  "openclaw": {
32
- "extensions": ["./dist/index.js"]
32
+ "extensions": [
33
+ "./dist/index.js"
34
+ ]
33
35
  },
34
36
  "devDependencies": {
35
37
  "@types/ws": "^8.0.0",
package/server.js CHANGED
@@ -2357,17 +2357,11 @@ class GatewayClient {
2357
2357
  // Update thread updated_at
2358
2358
  db.prepare('UPDATE threads SET updated_at = ? WHERE id = ?').run(now, parsed.threadId);
2359
2359
 
2360
- // Check if thread is active in any browser client
2361
- const isActive = [...this.browserClients.values()].some(
2362
- c => c.activeWorkspace === parsed.workspace && c.activeThreadId === parsed.threadId
2363
- );
2364
-
2365
- // Track unread message if thread not active in any browser
2366
- if (!isActive) {
2367
- db.prepare('INSERT OR IGNORE INTO unread_messages (thread_id, message_id, created_at) VALUES (?, ?, ?)').run(parsed.threadId, messageId, now);
2368
- // Recount from table (always accurate)
2369
- syncThreadUnreadCount(db, parsed.threadId);
2370
- }
2360
+ // Always mark as unread server-side. The browser client is responsible
2361
+ // for sending read receipts (active-thread) to clear unreads — same
2362
+ // pattern as Slack/Discord. Server doesn't track viewport state.
2363
+ db.prepare('INSERT OR IGNORE INTO unread_messages (thread_id, message_id, created_at) VALUES (?, ?, ?)').run(parsed.threadId, messageId, now);
2364
+ syncThreadUnreadCount(db, parsed.threadId);
2371
2365
 
2372
2366
  // Get thread title and unread info for notification
2373
2367
  const threadInfo = db.prepare('SELECT title FROM threads WHERE id = ?').get(parsed.threadId);
@@ -2387,23 +2381,21 @@ class GatewayClient {
2387
2381
  unreadCount
2388
2382
  }));
2389
2383
 
2390
- // Broadcast unread-update for badge/notification sync (all clients)
2391
- if (!isActive) {
2392
- const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
2393
- this.broadcastToBrowsers(JSON.stringify({
2394
- type: 'shellchat',
2395
- event: 'unread-update',
2396
- workspace: parsed.workspace,
2397
- threadId: parsed.threadId,
2398
- messageId,
2399
- action: 'new',
2400
- unreadCount,
2401
- workspaceUnreadTotal,
2402
- title: threadInfo?.title || 'Chat',
2403
- preview,
2404
- timestamp: now
2405
- }));
2406
- }
2384
+ // Always broadcast unread-update browser sends read receipts to clear
2385
+ const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
2386
+ this.broadcastToBrowsers(JSON.stringify({
2387
+ type: 'shellchat',
2388
+ event: 'unread-update',
2389
+ workspace: parsed.workspace,
2390
+ threadId: parsed.threadId,
2391
+ messageId,
2392
+ action: 'new',
2393
+ unreadCount,
2394
+ workspaceUnreadTotal,
2395
+ title: threadInfo?.title || 'Chat',
2396
+ preview,
2397
+ timestamp: now
2398
+ }));
2407
2399
 
2408
2400
  console.log(`Saved assistant message to ${parsed.workspace}/${parsed.threadId} (seq: ${seq})`);
2409
2401
  } catch (e) {
@@ -2854,13 +2846,13 @@ class GatewayClient {
2854
2846
  }
2855
2847
 
2856
2848
  setActiveThread(ws, workspace, threadId) {
2857
- const client = this.browserClients.get(ws);
2849
+ const client = ws ? this.browserClients.get(ws) : null;
2858
2850
  if (client) {
2859
2851
  client.activeWorkspace = workspace;
2860
2852
  client.activeThreadId = threadId;
2861
2853
  }
2862
2854
 
2863
- // Auto-clear unreads: opening a thread = reading it
2855
+ // Auto-clear unreads: opening a thread = reading it (read receipt)
2864
2856
  if (workspace && threadId) {
2865
2857
  try {
2866
2858
  const wsData = getWorkspaces();
@@ -3620,19 +3612,15 @@ export function createApp(config = {}) {
3620
3612
  try {
3621
3613
  db.prepare(`INSERT INTO messages (id, thread_id, role, content, status, timestamp, created_at) VALUES (?, ?, 'assistant', ?, 'sent', ?, ?) ON CONFLICT(id) DO UPDATE SET content = excluded.content, timestamp = excluded.timestamp`).run(messageId, parsed.threadId, content, now, now);
3622
3614
  db.prepare('UPDATE threads SET updated_at = ? WHERE id = ?').run(now, parsed.threadId);
3623
- const isActive = [...this.browserClients.values()].some(c => c.activeWorkspace === parsed.workspace && c.activeThreadId === parsed.threadId);
3624
- if (!isActive) {
3625
- db.prepare('INSERT OR IGNORE INTO unread_messages (thread_id, message_id, created_at) VALUES (?, ?, ?)').run(parsed.threadId, messageId, now);
3626
- syncThreadUnreadCount(db, parsed.threadId);
3627
- }
3615
+ // Always mark as unread browser sends read receipts to clear
3616
+ db.prepare('INSERT OR IGNORE INTO unread_messages (thread_id, message_id, created_at) VALUES (?, ?, ?)').run(parsed.threadId, messageId, now);
3617
+ syncThreadUnreadCount(db, parsed.threadId);
3628
3618
  const threadInfo = db.prepare('SELECT title FROM threads WHERE id = ?').get(parsed.threadId);
3629
3619
  const unreadCount = db.prepare('SELECT COUNT(*) as c FROM unread_messages WHERE thread_id = ?').get(parsed.threadId).c;
3630
3620
  const preview = content.length > 120 ? content.substring(0, 120) + '...' : content;
3631
3621
  this.broadcastToBrowsers(JSON.stringify({ type: 'shellchat', event: 'message-saved', threadId: parsed.threadId, workspace: parsed.workspace, messageId, timestamp: now, title: threadInfo?.title || 'Chat', preview, unreadCount }));
3632
- if (!isActive) {
3633
- const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
3634
- this.broadcastToBrowsers(JSON.stringify({ type: 'shellchat', event: 'unread-update', workspace: parsed.workspace, threadId: parsed.threadId, messageId, action: 'new', unreadCount, workspaceUnreadTotal, title: threadInfo?.title || 'Chat', preview, timestamp: now }));
3635
- }
3622
+ const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
3623
+ this.broadcastToBrowsers(JSON.stringify({ type: 'shellchat', event: 'unread-update', workspace: parsed.workspace, threadId: parsed.threadId, messageId, action: 'new', unreadCount, workspaceUnreadTotal, title: threadInfo?.title || 'Chat', preview, timestamp: now }));
3636
3624
  console.log(`Saved assistant message to ${parsed.workspace}/${parsed.threadId} (seq: ${seq})`);
3637
3625
  } catch (e) { console.error(`Failed to save assistant message:`, e.message); }
3638
3626
  }
@@ -3846,7 +3834,7 @@ export function createApp(config = {}) {
3846
3834
  removeBrowserClient(ws) { this.browserClients.delete(ws); }
3847
3835
 
3848
3836
  setActiveThread(ws, workspace, threadId) {
3849
- const client = this.browserClients.get(ws);
3837
+ const client = ws ? this.browserClients.get(ws) : null;
3850
3838
  if (client) { client.activeWorkspace = workspace; client.activeThreadId = threadId; }
3851
3839
  if (workspace && threadId) {
3852
3840
  try {
@@ -3944,7 +3932,7 @@ export function createApp(config = {}) {
3944
3932
  if (method === 'GET' && urlPath === '/api/export') return _handleExport(req, res);
3945
3933
  if (method === 'POST' && urlPath === '/api/import') return await _handleImport(req, res);
3946
3934
  if (method === 'POST' && urlPath === '/api/active-thread') {
3947
- const body = JSON.parse(await parseBody(req));
3935
+ const body = await parseBody(req);
3948
3936
  const { threadId, workspace } = body;
3949
3937
  if (threadId && workspace) _gatewayClient.setActiveThread(null, workspace, threadId);
3950
3938
  return send(res, 200, { ok: true });