@clawchatsai/connector 0.0.13 → 0.0.14
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/dist/index.d.ts +2 -2
- package/dist/index.js +34 -34
- package/dist/migrate.d.ts +1 -1
- package/dist/migrate.js +1 -1
- package/dist/shim.js +1 -1
- package/dist/signaling-client.d.ts +1 -1
- package/dist/signaling-client.js +1 -1
- package/dist/webrtc-peer.d.ts +1 -1
- package/dist/webrtc-peer.js +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -2
- package/server.js +32 -32
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
2
|
+
* @clawchatsai/connector — OpenClaw plugin entry point
|
|
3
3
|
*
|
|
4
|
-
* Registers
|
|
4
|
+
* Registers ClawChats as a gateway plugin, providing:
|
|
5
5
|
* - Local HTTP API bridge via createApp()
|
|
6
6
|
* - WebRTC DataChannel for browser connections
|
|
7
7
|
* - Signaling client for NAT traversal
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
2
|
+
* @clawchatsai/connector — OpenClaw plugin entry point
|
|
3
3
|
*
|
|
4
|
-
* Registers
|
|
4
|
+
* Registers ClawChats as a gateway plugin, providing:
|
|
5
5
|
* - Local HTTP API bridge via createApp()
|
|
6
6
|
* - WebRTC DataChannel for browser connections
|
|
7
7
|
* - Signaling client for NAT traversal
|
|
@@ -32,7 +32,7 @@ let _stopRequested = false;
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
// Config helpers
|
|
34
34
|
// ---------------------------------------------------------------------------
|
|
35
|
-
const CONFIG_DIR = path.join(process.env.HOME || '/root', '.openclaw', '
|
|
35
|
+
const CONFIG_DIR = path.join(process.env.HOME || '/root', '.openclaw', 'clawchats');
|
|
36
36
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
37
37
|
const RUNTIME_FILE = path.join(CONFIG_DIR, 'runtime.json');
|
|
38
38
|
function loadConfig() {
|
|
@@ -73,11 +73,11 @@ async function ensureNativeModules(ctx) {
|
|
|
73
73
|
ctx.logger.error('Try running manually: cd ~/.openclaw/extensions/connector && npm rebuild better-sqlite3');
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
async function
|
|
76
|
+
async function startClawChats(ctx, api) {
|
|
77
77
|
_stopRequested = false;
|
|
78
78
|
let config = loadConfig();
|
|
79
79
|
if (!config) {
|
|
80
|
-
ctx.logger.info('
|
|
80
|
+
ctx.logger.info('ClawChats not configured. Waiting for setup...');
|
|
81
81
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
82
82
|
while (!config && !_stopRequested) {
|
|
83
83
|
await new Promise(r => setTimeout(r, 2000));
|
|
@@ -85,7 +85,7 @@ async function startShellChat(ctx, api) {
|
|
|
85
85
|
}
|
|
86
86
|
if (_stopRequested || !config)
|
|
87
87
|
return;
|
|
88
|
-
ctx.logger.info('Setup detected — connecting to
|
|
88
|
+
ctx.logger.info('Setup detected — connecting to ClawChats...');
|
|
89
89
|
}
|
|
90
90
|
// 1. Check for updates
|
|
91
91
|
const update = await checkForUpdates();
|
|
@@ -95,7 +95,7 @@ async function startShellChat(ctx, api) {
|
|
|
95
95
|
try {
|
|
96
96
|
await performUpdate();
|
|
97
97
|
ctx.logger.info(`Updated to ${update.latest}. Requesting graceful restart...`);
|
|
98
|
-
api.runtime.requestRestart?.('
|
|
98
|
+
api.runtime.requestRestart?.('clawchats update');
|
|
99
99
|
return; // will restart with new version
|
|
100
100
|
}
|
|
101
101
|
catch (e) {
|
|
@@ -108,14 +108,14 @@ async function startShellChat(ctx, api) {
|
|
|
108
108
|
const gwAuth = gwCfg?.['gateway']?.['auth'];
|
|
109
109
|
const gatewayToken = gwAuth?.['token'] || config.gatewayToken || '';
|
|
110
110
|
if (!gatewayToken) {
|
|
111
|
-
ctx.logger.error('No gateway token available. Re-run: openclaw
|
|
111
|
+
ctx.logger.error('No gateway token available. Re-run: openclaw clawchats setup <token>');
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
// 3. Ensure native modules are built (OpenClaw installs with --ignore-scripts)
|
|
115
115
|
await ensureNativeModules(ctx);
|
|
116
116
|
// 4. Import server.js and create app instance with plugin paths
|
|
117
|
-
const dataDir = path.join(ctx.stateDir, '
|
|
118
|
-
const uploadsDir = path.join(ctx.stateDir, '
|
|
117
|
+
const dataDir = path.join(ctx.stateDir, 'clawchats', 'data');
|
|
118
|
+
const uploadsDir = path.join(ctx.stateDir, 'clawchats', 'uploads');
|
|
119
119
|
// Dynamic import of server.js (plain JS, no type declarations)
|
|
120
120
|
// @ts-expect-error — server.js is plain JS with no .d.ts
|
|
121
121
|
const serverModule = await import('../server.js');
|
|
@@ -228,11 +228,11 @@ async function startShellChat(ctx, api) {
|
|
|
228
228
|
}, null, 2), { mode: 0o600 });
|
|
229
229
|
ctx.logger.info(`Health endpoint on 127.0.0.1:${addr.port}`);
|
|
230
230
|
});
|
|
231
|
-
ctx.logger.info('
|
|
231
|
+
ctx.logger.info('ClawChats service started');
|
|
232
232
|
}
|
|
233
|
-
async function
|
|
233
|
+
async function stopClawChats(ctx) {
|
|
234
234
|
_stopRequested = true;
|
|
235
|
-
ctx.logger.info('
|
|
235
|
+
ctx.logger.info('ClawChats service stopping...');
|
|
236
236
|
// 0. Tear down health endpoint
|
|
237
237
|
if (healthServer) {
|
|
238
238
|
healthServer.close();
|
|
@@ -259,7 +259,7 @@ async function stopShellChat(ctx) {
|
|
|
259
259
|
// 4. Close SQLite databases
|
|
260
260
|
app?.shutdown();
|
|
261
261
|
app = null;
|
|
262
|
-
ctx.logger.info('
|
|
262
|
+
ctx.logger.info('ClawChats service stopped');
|
|
263
263
|
}
|
|
264
264
|
// ---------------------------------------------------------------------------
|
|
265
265
|
// DataChannel message handler (spec section 6.4)
|
|
@@ -386,7 +386,7 @@ function broadcastToClients(msg) {
|
|
|
386
386
|
// ---------------------------------------------------------------------------
|
|
387
387
|
function formatStatus() {
|
|
388
388
|
const lines = [];
|
|
389
|
-
lines.push(`
|
|
389
|
+
lines.push(`ClawChats Plugin v${PLUGIN_VERSION}`);
|
|
390
390
|
lines.push(`Gateway: ${app?.gatewayClient?.connected ? 'connected' : 'disconnected'}`);
|
|
391
391
|
lines.push(`Signaling: ${signaling?.isConnected ? 'connected' : 'disconnected'}`);
|
|
392
392
|
lines.push(`Clients: ${connectedClients.size}`);
|
|
@@ -410,7 +410,7 @@ async function handleSetup(token) {
|
|
|
410
410
|
console.error('Setup token has expired. Generate a new one from clawchats.ai.');
|
|
411
411
|
return;
|
|
412
412
|
}
|
|
413
|
-
console.log('Setting up
|
|
413
|
+
console.log('Setting up ClawChats...');
|
|
414
414
|
console.log(` Server: ${tokenData.serverUrl}`);
|
|
415
415
|
// Generate API key for signaling server auth
|
|
416
416
|
const { randomBytes } = await import('node:crypto');
|
|
@@ -467,7 +467,7 @@ async function handleSetup(token) {
|
|
|
467
467
|
const uploadsDir = path.join(CONFIG_DIR, 'uploads');
|
|
468
468
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
469
469
|
fs.mkdirSync(uploadsDir, { recursive: true });
|
|
470
|
-
console.log('
|
|
470
|
+
console.log(' ClawChats is ready!');
|
|
471
471
|
console.log(' Open clawchats.ai in your browser to start chatting.');
|
|
472
472
|
ws.close();
|
|
473
473
|
resolve();
|
|
@@ -497,7 +497,7 @@ async function handleStatus() {
|
|
|
497
497
|
runtime = JSON.parse(fs.readFileSync(RUNTIME_FILE, 'utf8'));
|
|
498
498
|
}
|
|
499
499
|
catch {
|
|
500
|
-
console.log('
|
|
500
|
+
console.log('ClawChats: offline (service not running)');
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
503
|
// Verify PID is alive
|
|
@@ -505,7 +505,7 @@ async function handleStatus() {
|
|
|
505
505
|
process.kill(runtime.pid, 0);
|
|
506
506
|
}
|
|
507
507
|
catch {
|
|
508
|
-
console.log('
|
|
508
|
+
console.log('ClawChats: offline (stale runtime file)');
|
|
509
509
|
try {
|
|
510
510
|
fs.unlinkSync(RUNTIME_FILE);
|
|
511
511
|
}
|
|
@@ -524,20 +524,20 @@ async function handleStatus() {
|
|
|
524
524
|
req.setTimeout(3000, () => { req.destroy(); reject(new Error('timeout')); });
|
|
525
525
|
});
|
|
526
526
|
const status = JSON.parse(body);
|
|
527
|
-
console.log(`
|
|
527
|
+
console.log(`ClawChats Plugin v${status.version}`);
|
|
528
528
|
console.log(`Uptime: ${Math.floor(status.uptime)}s`);
|
|
529
529
|
console.log(`Gateway: ${status.gateway.connected ? 'connected' : 'disconnected'}`);
|
|
530
530
|
console.log(`Signaling: ${status.signaling.connected ? 'connected' : 'disconnected'}`);
|
|
531
531
|
console.log(`Clients: ${status.clients.active}`);
|
|
532
532
|
}
|
|
533
533
|
catch {
|
|
534
|
-
console.log('
|
|
534
|
+
console.log('ClawChats: offline (could not reach service)');
|
|
535
535
|
}
|
|
536
536
|
}
|
|
537
537
|
async function handleReset() {
|
|
538
538
|
try {
|
|
539
539
|
fs.rmSync(CONFIG_DIR, { recursive: true, force: true });
|
|
540
|
-
console.log('
|
|
540
|
+
console.log('ClawChats data removed. Plugin disconnected.');
|
|
541
541
|
}
|
|
542
542
|
catch (e) {
|
|
543
543
|
console.error(`Reset failed: ${e.message}`);
|
|
@@ -554,7 +554,7 @@ async function handleImport(sourcePath) {
|
|
|
554
554
|
console.error(`Source must be a directory: ${resolvedSource}`);
|
|
555
555
|
return;
|
|
556
556
|
}
|
|
557
|
-
// Destination: ~/.openclaw/
|
|
557
|
+
// Destination: ~/.openclaw/clawchats/data/
|
|
558
558
|
const destDataDir = path.join(CONFIG_DIR, 'data');
|
|
559
559
|
fs.mkdirSync(destDataDir, { recursive: true });
|
|
560
560
|
// Import .db files
|
|
@@ -586,7 +586,7 @@ async function handleImport(sourcePath) {
|
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
588
|
// Also try to migrate config.json from the parent directory
|
|
589
|
-
// e.g. if source is ~/.openclaw/
|
|
589
|
+
// e.g. if source is ~/.openclaw/clawchats/data/, config is at ~/.openclaw/clawchats/config.json
|
|
590
590
|
const parentConfigPath = path.join(path.dirname(resolvedSource), 'config.json');
|
|
591
591
|
if (fs.existsSync(parentConfigPath)) {
|
|
592
592
|
try {
|
|
@@ -605,26 +605,26 @@ async function handleImport(sourcePath) {
|
|
|
605
605
|
// ---------------------------------------------------------------------------
|
|
606
606
|
const plugin = {
|
|
607
607
|
id: PLUGIN_ID,
|
|
608
|
-
name: '
|
|
609
|
-
description: 'Connects your gateway to
|
|
608
|
+
name: 'ClawChats',
|
|
609
|
+
description: 'Connects your gateway to ClawChats via WebRTC P2P',
|
|
610
610
|
register(api) {
|
|
611
611
|
// Background service: signaling + gateway bridge + future WebRTC
|
|
612
612
|
api.registerService({
|
|
613
613
|
id: 'connector-service',
|
|
614
|
-
start: (ctx) =>
|
|
615
|
-
stop: (ctx) =>
|
|
614
|
+
start: (ctx) => startClawChats(ctx, api),
|
|
615
|
+
stop: (ctx) => stopClawChats(ctx),
|
|
616
616
|
});
|
|
617
617
|
// CLI commands
|
|
618
618
|
api.registerCli((ctx) => {
|
|
619
|
-
const cmd = ctx.program.command('
|
|
619
|
+
const cmd = ctx.program.command('clawchats');
|
|
620
620
|
cmd.command('setup <token>')
|
|
621
|
-
.description('Set up
|
|
621
|
+
.description('Set up ClawChats with a setup token')
|
|
622
622
|
.action((token) => handleSetup(String(token)));
|
|
623
623
|
cmd.command('status')
|
|
624
|
-
.description('Show
|
|
624
|
+
.description('Show ClawChats connection status')
|
|
625
625
|
.action(() => handleStatus());
|
|
626
626
|
cmd.command('reset')
|
|
627
|
-
.description('Disconnect and remove all
|
|
627
|
+
.description('Disconnect and remove all ClawChats data')
|
|
628
628
|
.action(() => handleReset());
|
|
629
629
|
cmd.command('import <path>')
|
|
630
630
|
.description('Import databases and config from a folder (e.g. migrate from old data directory)')
|
|
@@ -632,8 +632,8 @@ const plugin = {
|
|
|
632
632
|
});
|
|
633
633
|
// Slash command for status from any channel
|
|
634
634
|
api.registerCommand({
|
|
635
|
-
name: '
|
|
636
|
-
description: 'Show
|
|
635
|
+
name: 'clawchats',
|
|
636
|
+
description: 'Show ClawChats tunnel status',
|
|
637
637
|
handler: () => ({ text: formatStatus() }),
|
|
638
638
|
});
|
|
639
639
|
},
|
package/dist/migrate.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Schema migration runner for the
|
|
2
|
+
* Schema migration runner for the ClawChats plugin SQLite database.
|
|
3
3
|
*
|
|
4
4
|
* - Tracks schema version in a `_schema_version` table
|
|
5
5
|
* - Runs migrations sequentially from current version to target
|
package/dist/migrate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Schema migration runner for the
|
|
2
|
+
* Schema migration runner for the ClawChats plugin SQLite database.
|
|
3
3
|
*
|
|
4
4
|
* - Tracks schema version in a `_schema_version` table
|
|
5
5
|
* - Runs migrations sequentially from current version to target
|
package/dist/shim.js
CHANGED
|
@@ -46,7 +46,7 @@ class FakeReq extends Readable {
|
|
|
46
46
|
}
|
|
47
47
|
else if (parsed && parsed['_multipart']) {
|
|
48
48
|
// Multipart form data: { _multipart: true, fields: { key: string | { filename, contentType, data } } }
|
|
49
|
-
const boundary = '----
|
|
49
|
+
const boundary = '----ClawChatsBoundary' + Date.now();
|
|
50
50
|
this.headers['content-type'] = `multipart/form-data; boundary=${boundary}`;
|
|
51
51
|
const fields = parsed['fields'] || {};
|
|
52
52
|
const parts = [];
|
package/dist/signaling-client.js
CHANGED
package/dist/webrtc-peer.d.ts
CHANGED
|
@@ -109,7 +109,7 @@ export declare class WebRTCPeerManager extends EventEmitter {
|
|
|
109
109
|
handleIceCandidate(connectionId: string, candidate: unknown): void;
|
|
110
110
|
/**
|
|
111
111
|
* Close all active peer connections and DataChannels.
|
|
112
|
-
* Called during plugin shutdown (
|
|
112
|
+
* Called during plugin shutdown (stopClawChats).
|
|
113
113
|
*/
|
|
114
114
|
closeAll(): void;
|
|
115
115
|
/**
|
package/dist/webrtc-peer.js
CHANGED
|
@@ -194,7 +194,7 @@ export class WebRTCPeerManager extends EventEmitter {
|
|
|
194
194
|
}
|
|
195
195
|
/**
|
|
196
196
|
* Close all active peer connections and DataChannels.
|
|
197
|
-
* Called during plugin shutdown (
|
|
197
|
+
* Called during plugin shutdown (stopClawChats).
|
|
198
198
|
*/
|
|
199
199
|
closeAll() {
|
|
200
200
|
console.log(`[WebRTCPeerManager] Closing all connections (${this.peerConnections.size} peers, ${this.activeChannels.size} channels)`);
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "connector",
|
|
3
|
-
"name": "
|
|
4
|
-
"description": "Connects your OpenClaw gateway to
|
|
3
|
+
"name": "ClawChats",
|
|
4
|
+
"description": "Connects your OpenClaw gateway to ClawChats via WebRTC P2P",
|
|
5
5
|
"kind": "integration",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawchatsai/connector",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "ClawChats OpenClaw plugin — P2P tunnel + local API bridge",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
package/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ClawChats Backend Server
|
|
2
2
|
// Single-file Node.js HTTP server with SQLite storage
|
|
3
3
|
// See specs/backend-session-architecture.md for full spec
|
|
4
4
|
|
|
@@ -64,7 +64,7 @@ function _buildDeviceAuth(identity, { clientId, clientMode, role, scopes, token,
|
|
|
64
64
|
|
|
65
65
|
// ─── Configuration ──────────────────────────────────────────────────────────
|
|
66
66
|
|
|
67
|
-
const PORT = parseInt(process.env.SHELLCHAT_PORT || '3001', 10);
|
|
67
|
+
const PORT = parseInt(process.env.CLAWCHATS_PORT || process.env.SHELLCHAT_PORT || '3001', 10);
|
|
68
68
|
const DATA_DIR = path.join(__dirname, 'data');
|
|
69
69
|
const UPLOADS_DIR = path.join(__dirname, 'uploads');
|
|
70
70
|
const WORKSPACES_FILE = path.join(DATA_DIR, 'workspaces.json');
|
|
@@ -174,9 +174,9 @@ function parseConfigField(field) {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// Load auth token from config.js or env var
|
|
177
|
-
let AUTH_TOKEN = process.env.SHELLCHAT_AUTH_TOKEN || parseConfigField('authToken') || '';
|
|
177
|
+
let AUTH_TOKEN = process.env.CLAWCHATS_AUTH_TOKEN || process.env.SHELLCHAT_AUTH_TOKEN || parseConfigField('authToken') || '';
|
|
178
178
|
if (!AUTH_TOKEN) {
|
|
179
|
-
console.error('WARNING: No auth token configured. Set
|
|
179
|
+
console.error('WARNING: No auth token configured. Set CLAWCHATS_AUTH_TOKEN or create config.js');
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// Load gateway WebSocket URL
|
|
@@ -883,7 +883,7 @@ async function handleMarkMessagesRead(req, res, params) {
|
|
|
883
883
|
// Broadcast unread-update to ALL browser clients (so other tabs/devices sync)
|
|
884
884
|
const workspace = getWorkspaces().active;
|
|
885
885
|
gatewayClient.broadcastToBrowsers(JSON.stringify({
|
|
886
|
-
type: '
|
|
886
|
+
type: 'clawchats',
|
|
887
887
|
event: 'unread-update',
|
|
888
888
|
workspace,
|
|
889
889
|
threadId,
|
|
@@ -2369,7 +2369,7 @@ class GatewayClient {
|
|
|
2369
2369
|
|
|
2370
2370
|
saveAssistantMessage(sessionKey, message, seq) {
|
|
2371
2371
|
const parsed = parseSessionKey(sessionKey);
|
|
2372
|
-
if (!parsed) return; // Non-
|
|
2372
|
+
if (!parsed) return; // Non-ClawChats session key, silently ignore
|
|
2373
2373
|
|
|
2374
2374
|
// Guard: verify workspace still exists
|
|
2375
2375
|
const ws = getWorkspaces();
|
|
@@ -2424,7 +2424,7 @@ class GatewayClient {
|
|
|
2424
2424
|
|
|
2425
2425
|
// Broadcast message-saved for active thread reload
|
|
2426
2426
|
this.broadcastToBrowsers(JSON.stringify({
|
|
2427
|
-
type: '
|
|
2427
|
+
type: 'clawchats',
|
|
2428
2428
|
event: 'message-saved',
|
|
2429
2429
|
threadId: parsed.threadId,
|
|
2430
2430
|
workspace: parsed.workspace,
|
|
@@ -2438,7 +2438,7 @@ class GatewayClient {
|
|
|
2438
2438
|
// Always broadcast unread-update — browser sends read receipts to clear
|
|
2439
2439
|
const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
|
|
2440
2440
|
this.broadcastToBrowsers(JSON.stringify({
|
|
2441
|
-
type: '
|
|
2441
|
+
type: 'clawchats',
|
|
2442
2442
|
event: 'unread-update',
|
|
2443
2443
|
workspace: parsed.workspace,
|
|
2444
2444
|
threadId: parsed.threadId,
|
|
@@ -2616,7 +2616,7 @@ class GatewayClient {
|
|
|
2616
2616
|
if (!parsed) return;
|
|
2617
2617
|
|
|
2618
2618
|
this.broadcastToBrowsers(JSON.stringify({
|
|
2619
|
-
type: '
|
|
2619
|
+
type: 'clawchats',
|
|
2620
2620
|
event: 'agent-activity',
|
|
2621
2621
|
workspace: parsed.workspace,
|
|
2622
2622
|
threadId: parsed.threadId,
|
|
@@ -2761,7 +2761,7 @@ class GatewayClient {
|
|
|
2761
2761
|
|
|
2762
2762
|
// Notify browsers to re-render this message with activity data
|
|
2763
2763
|
this.broadcastToBrowsers(JSON.stringify({
|
|
2764
|
-
type: '
|
|
2764
|
+
type: 'clawchats',
|
|
2765
2765
|
event: 'activity-saved',
|
|
2766
2766
|
workspace: parsed.workspace,
|
|
2767
2767
|
threadId: parsed.threadId,
|
|
@@ -2844,7 +2844,7 @@ class GatewayClient {
|
|
|
2844
2844
|
|
|
2845
2845
|
broadcastGatewayStatus(connected) {
|
|
2846
2846
|
const msg = JSON.stringify({
|
|
2847
|
-
type: '
|
|
2847
|
+
type: 'clawchats',
|
|
2848
2848
|
event: 'gateway-status',
|
|
2849
2849
|
connected
|
|
2850
2850
|
});
|
|
@@ -2873,7 +2873,7 @@ class GatewayClient {
|
|
|
2873
2873
|
// Send current gateway status
|
|
2874
2874
|
if (ws.readyState === WS.OPEN) {
|
|
2875
2875
|
ws.send(JSON.stringify({
|
|
2876
|
-
type: '
|
|
2876
|
+
type: 'clawchats',
|
|
2877
2877
|
event: 'gateway-status',
|
|
2878
2878
|
connected: this.connected
|
|
2879
2879
|
}));
|
|
@@ -2887,7 +2887,7 @@ class GatewayClient {
|
|
|
2887
2887
|
}
|
|
2888
2888
|
if (streams.length > 0) {
|
|
2889
2889
|
ws.send(JSON.stringify({
|
|
2890
|
-
type: '
|
|
2890
|
+
type: 'clawchats',
|
|
2891
2891
|
event: 'stream-sync',
|
|
2892
2892
|
streams
|
|
2893
2893
|
}));
|
|
@@ -2924,7 +2924,7 @@ class GatewayClient {
|
|
|
2924
2924
|
// Broadcast unread-update clear to ALL browser clients
|
|
2925
2925
|
const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
|
|
2926
2926
|
this.broadcastToBrowsers(JSON.stringify({
|
|
2927
|
-
type: '
|
|
2927
|
+
type: 'clawchats',
|
|
2928
2928
|
event: 'unread-update',
|
|
2929
2929
|
workspace,
|
|
2930
2930
|
threadId,
|
|
@@ -2952,7 +2952,7 @@ function syncThreadUnreadCount(db, threadId) {
|
|
|
2952
2952
|
function parseSessionKey(sessionKey) {
|
|
2953
2953
|
if (!sessionKey) return null;
|
|
2954
2954
|
const match = sessionKey.match(/^agent:main:([^:]+):chat:([^:]+)$/);
|
|
2955
|
-
if (!match) return null; // Non-
|
|
2955
|
+
if (!match) return null; // Non-ClawChats keys — silently ignore
|
|
2956
2956
|
return { workspace: match[1], threadId: match[2] };
|
|
2957
2957
|
}
|
|
2958
2958
|
|
|
@@ -2973,7 +2973,7 @@ const gatewayClient = new GatewayClient();
|
|
|
2973
2973
|
|
|
2974
2974
|
// ─── createApp Factory ───────────────────────────────────────────────────────
|
|
2975
2975
|
// Returns an isolated instance of the app state + handlers.
|
|
2976
|
-
// Used by the plugin (signaling/index.js) to embed
|
|
2976
|
+
// Used by the plugin (signaling/index.js) to embed ClawChats logic without
|
|
2977
2977
|
// spinning up a standalone HTTP server.
|
|
2978
2978
|
|
|
2979
2979
|
export function createApp(config = {}) {
|
|
@@ -2986,7 +2986,7 @@ export function createApp(config = {}) {
|
|
|
2986
2986
|
|
|
2987
2987
|
let _AUTH_TOKEN = config.authToken !== undefined
|
|
2988
2988
|
? config.authToken
|
|
2989
|
-
: (process.env.SHELLCHAT_AUTH_TOKEN || parseConfigField('authToken') || '');
|
|
2989
|
+
: (process.env.CLAWCHATS_AUTH_TOKEN || process.env.SHELLCHAT_AUTH_TOKEN || parseConfigField('authToken') || '');
|
|
2990
2990
|
|
|
2991
2991
|
// Separate token for gateway WS auth (falls back to _AUTH_TOKEN for direct mode)
|
|
2992
2992
|
const _GATEWAY_TOKEN = config.gatewayToken !== undefined
|
|
@@ -3232,7 +3232,7 @@ export function createApp(config = {}) {
|
|
|
3232
3232
|
const remaining = syncThreadUnreadCount(db, threadId);
|
|
3233
3233
|
const workspace = _getWorkspaces().active;
|
|
3234
3234
|
_gatewayClient.broadcastToBrowsers(JSON.stringify({
|
|
3235
|
-
type: '
|
|
3235
|
+
type: 'clawchats', event: 'unread-update', workspace, threadId,
|
|
3236
3236
|
action: 'read', messageIds, unreadCount: remaining, timestamp: Date.now()
|
|
3237
3237
|
}));
|
|
3238
3238
|
send(res, 200, { unread_count: remaining });
|
|
@@ -3679,9 +3679,9 @@ export function createApp(config = {}) {
|
|
|
3679
3679
|
const threadInfo = db.prepare('SELECT title FROM threads WHERE id = ?').get(parsed.threadId);
|
|
3680
3680
|
const unreadCount = db.prepare('SELECT COUNT(*) as c FROM unread_messages WHERE thread_id = ?').get(parsed.threadId).c;
|
|
3681
3681
|
const preview = content.length > 120 ? content.substring(0, 120) + '...' : content;
|
|
3682
|
-
this.broadcastToBrowsers(JSON.stringify({ type: '
|
|
3682
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'message-saved', threadId: parsed.threadId, workspace: parsed.workspace, messageId, timestamp: now, title: threadInfo?.title || 'Chat', preview, unreadCount }));
|
|
3683
3683
|
const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
|
|
3684
|
-
this.broadcastToBrowsers(JSON.stringify({ type: '
|
|
3684
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'unread-update', workspace: parsed.workspace, threadId: parsed.threadId, messageId, action: 'new', unreadCount, workspaceUnreadTotal, title: threadInfo?.title || 'Chat', preview, timestamp: now }));
|
|
3685
3685
|
console.log(`Saved assistant message to ${parsed.workspace}/${parsed.threadId} (seq: ${seq})`);
|
|
3686
3686
|
} catch (e) { console.error(`Failed to save assistant message:`, e.message); }
|
|
3687
3687
|
}
|
|
@@ -3759,7 +3759,7 @@ export function createApp(config = {}) {
|
|
|
3759
3759
|
broadcastActivityUpdate(runId, log) {
|
|
3760
3760
|
const parsed = log.sessionKey ? parseSessionKey(log.sessionKey) : null;
|
|
3761
3761
|
if (!parsed) return;
|
|
3762
|
-
this.broadcastToBrowsers(JSON.stringify({ type: '
|
|
3762
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'agent-activity', workspace: parsed.workspace, threadId: parsed.threadId, runId, steps: log.steps, summary: this.generateActivitySummary(log.steps) }));
|
|
3763
3763
|
}
|
|
3764
3764
|
|
|
3765
3765
|
finalizeActivityLog(runId, log) {
|
|
@@ -3824,7 +3824,7 @@ export function createApp(config = {}) {
|
|
|
3824
3824
|
metadata.activitySummary = this.generateActivitySummary(log.steps);
|
|
3825
3825
|
db.prepare('UPDATE messages SET metadata = ? WHERE id = ?').run(JSON.stringify(metadata), msg.id);
|
|
3826
3826
|
console.log(`[ActivityLog] Saved ${toolSteps.length} tool steps for message ${msg.id}`);
|
|
3827
|
-
this.broadcastToBrowsers(JSON.stringify({ type: '
|
|
3827
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'activity-saved', workspace: parsed.workspace, threadId: parsed.threadId, messageId: msg.id }));
|
|
3828
3828
|
}
|
|
3829
3829
|
}
|
|
3830
3830
|
} catch (e) { console.error('Failed to save activity log:', e.message); }
|
|
@@ -3864,7 +3864,7 @@ export function createApp(config = {}) {
|
|
|
3864
3864
|
}
|
|
3865
3865
|
|
|
3866
3866
|
broadcastGatewayStatus(connected) {
|
|
3867
|
-
this.broadcastToBrowsers(JSON.stringify({ type: '
|
|
3867
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'gateway-status', connected }));
|
|
3868
3868
|
}
|
|
3869
3869
|
|
|
3870
3870
|
sendToGateway(data) {
|
|
@@ -3883,12 +3883,12 @@ export function createApp(config = {}) {
|
|
|
3883
3883
|
addBrowserClient(ws) {
|
|
3884
3884
|
this.browserClients.set(ws, { activeWorkspace: null, activeThreadId: null });
|
|
3885
3885
|
if (ws.readyState === WS.OPEN) {
|
|
3886
|
-
ws.send(JSON.stringify({ type: '
|
|
3886
|
+
ws.send(JSON.stringify({ type: 'clawchats', event: 'gateway-status', connected: this.connected }));
|
|
3887
3887
|
const streams = [];
|
|
3888
3888
|
for (const [sessionKey, state] of this.streamState.entries()) {
|
|
3889
3889
|
if (state.state === 'streaming') streams.push({ sessionKey, threadId: state.threadId, buffer: state.buffer });
|
|
3890
3890
|
}
|
|
3891
|
-
if (streams.length > 0) ws.send(JSON.stringify({ type: '
|
|
3891
|
+
if (streams.length > 0) ws.send(JSON.stringify({ type: 'clawchats', event: 'stream-sync', streams }));
|
|
3892
3892
|
}
|
|
3893
3893
|
}
|
|
3894
3894
|
|
|
@@ -3908,7 +3908,7 @@ export function createApp(config = {}) {
|
|
|
3908
3908
|
if (deleted.changes > 0) {
|
|
3909
3909
|
syncThreadUnreadCount(db, threadId);
|
|
3910
3910
|
const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
|
|
3911
|
-
this.broadcastToBrowsers(JSON.stringify({ type: '
|
|
3911
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'unread-update', workspace, threadId, action: 'clear', unreadCount: 0, workspaceUnreadTotal, timestamp: Date.now() }));
|
|
3912
3912
|
}
|
|
3913
3913
|
} catch (e) { console.error('Failed to auto-clear unreads on active-thread:', e.message); }
|
|
3914
3914
|
}
|
|
@@ -4024,7 +4024,7 @@ export function createApp(config = {}) {
|
|
|
4024
4024
|
const token = msg.params?.auth?.token;
|
|
4025
4025
|
if (token === _AUTH_TOKEN || !_AUTH_TOKEN) {
|
|
4026
4026
|
console.log('Browser client authenticated');
|
|
4027
|
-
ws.send(JSON.stringify({ type: 'res', id: msg.id, ok: true, payload: { type: 'hello-ok', protocol: 3, server: { version: '0.1.0', host: '
|
|
4027
|
+
ws.send(JSON.stringify({ type: 'res', id: msg.id, ok: true, payload: { type: 'hello-ok', protocol: 3, server: { version: '0.1.0', host: 'clawchats-backend' } } }));
|
|
4028
4028
|
} else {
|
|
4029
4029
|
console.log('Browser client auth failed');
|
|
4030
4030
|
ws.send(JSON.stringify({ type: 'res', id: msg.id, ok: false, error: { code: 'AUTH_FAILED', message: 'Invalid auth token' } }));
|
|
@@ -4032,12 +4032,12 @@ export function createApp(config = {}) {
|
|
|
4032
4032
|
}
|
|
4033
4033
|
return;
|
|
4034
4034
|
}
|
|
4035
|
-
if (msg.type === 'shellchat') {
|
|
4035
|
+
if (msg.type === 'clawchats' || msg.type === 'shellchat') { // backward compat: accept legacy 'shellchat' type
|
|
4036
4036
|
if (msg.action === 'active-thread') { _gatewayClient.setActiveThread(ws, msg.workspace, msg.threadId); console.log(`Browser client set active thread: ${msg.workspace}/${msg.threadId}`); return; }
|
|
4037
|
-
if (msg.action === 'debug-start') { const result = _debugLogger.start(msg.ts, ws); if (result.error === 'already-active') ws.send(JSON.stringify({ type: '
|
|
4038
|
-
if (msg.action === 'debug-dump') { const { sessionId, files } = _debugLogger.saveDump(msg); ws.send(JSON.stringify({ type: '
|
|
4037
|
+
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; }
|
|
4038
|
+
if (msg.action === 'debug-dump') { const { sessionId, files } = _debugLogger.saveDump(msg); ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-saved', sessionId, files })); return; }
|
|
4039
4039
|
}
|
|
4040
|
-
} catch { /* Not JSON or not a
|
|
4040
|
+
} catch { /* Not JSON or not a ClawChats message, forward to gateway */ }
|
|
4041
4041
|
_gatewayClient.sendToGateway(msgStr);
|
|
4042
4042
|
});
|
|
4043
4043
|
|
|
@@ -4086,7 +4086,7 @@ if (isDirectRun) {
|
|
|
4086
4086
|
});
|
|
4087
4087
|
|
|
4088
4088
|
server.listen(PORT, () => {
|
|
4089
|
-
console.log(`
|
|
4089
|
+
console.log(`ClawChats backend listening on port ${PORT}`);
|
|
4090
4090
|
console.log(`Active workspace: ${app.getWorkspaces().active}`);
|
|
4091
4091
|
console.log(`Data dir: ${app.dataDir}`);
|
|
4092
4092
|
|