@clawchatsai/connector 0.0.45 → 0.0.47
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/package.json +1 -1
- package/server.js +4 -67
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -3969,7 +3969,6 @@ export function createApp(config = {}) {
|
|
|
3969
3969
|
this._externalBroadcastTargets = [];
|
|
3970
3970
|
this.streamState = new Map();
|
|
3971
3971
|
this.activityLogs = new Map();
|
|
3972
|
-
this._pendingMediaUrls = new Map(); // sessionKey → string[] of MEDIA: paths captured during streaming
|
|
3973
3972
|
setInterval(() => {
|
|
3974
3973
|
const cutoff = Date.now() - 10 * 60 * 1000;
|
|
3975
3974
|
for (const [runId, log] of this.activityLogs) {
|
|
@@ -4066,42 +4065,6 @@ export function createApp(config = {}) {
|
|
|
4066
4065
|
if (!content || !content.trim()) { console.log(`Skipping empty assistant response for thread ${parsed.threadId}`); return; }
|
|
4067
4066
|
const now = Date.now();
|
|
4068
4067
|
|
|
4069
|
-
// Check if the final assembled message content contains MEDIA: lines
|
|
4070
|
-
console.log('[ClawChats] saveAssistantMessage hasMedia:', content.includes('MEDIA:'), 'msgKeys:', Object.keys(message||{}), 'msgMediaUrls:', JSON.stringify(message?.mediaUrls), 'msgMediaUrl:', message?.mediaUrl);
|
|
4071
|
-
|
|
4072
|
-
// Extract MEDIA: paths from the final complete message content (if gateway preserves them here)
|
|
4073
|
-
let attachments = null;
|
|
4074
|
-
const mediaMatches = [...content.matchAll(/^MEDIA:\s*(\S+)/gm)].map(m => m[1].trim()).filter(p => /\.\w{1,10}$/.test(p));
|
|
4075
|
-
if (mediaMatches.length > 0) {
|
|
4076
|
-
console.log('[ClawChats] MEDIA paths from final content:', JSON.stringify(mediaMatches));
|
|
4077
|
-
attachments = mediaMatches.map(filePath => {
|
|
4078
|
-
const name = path.basename(filePath);
|
|
4079
|
-
const ext = (name.split('.').pop() || '').toLowerCase();
|
|
4080
|
-
const type = ['jpg','jpeg','png','gif','webp','svg','bmp','ico'].includes(ext) ? 'image'
|
|
4081
|
-
: ['mp3','wav','ogg','aac','m4a','flac','opus'].includes(ext) ? 'audio'
|
|
4082
|
-
: ['mp4','webm','mov','avi','mkv'].includes(ext) ? 'video' : 'file';
|
|
4083
|
-
return { path: filePath, name, type };
|
|
4084
|
-
});
|
|
4085
|
-
}
|
|
4086
|
-
|
|
4087
|
-
// Fallback: use MEDIA: paths captured from delta stream
|
|
4088
|
-
if (!attachments) {
|
|
4089
|
-
const paths = this._pendingMediaUrls.get(sessionKey);
|
|
4090
|
-
this._pendingMediaUrls.delete(sessionKey);
|
|
4091
|
-
if (paths?.length) {
|
|
4092
|
-
attachments = paths.map(filePath => {
|
|
4093
|
-
const name = path.basename(filePath);
|
|
4094
|
-
const ext = (name.split('.').pop() || '').toLowerCase();
|
|
4095
|
-
const type = ['jpg','jpeg','png','gif','webp','svg','bmp','ico'].includes(ext) ? 'image'
|
|
4096
|
-
: ['mp3','wav','ogg','aac','m4a','flac','opus'].includes(ext) ? 'audio'
|
|
4097
|
-
: ['mp4','webm','mov','avi','mkv'].includes(ext) ? 'video' : 'file';
|
|
4098
|
-
return { path: filePath, name, type };
|
|
4099
|
-
});
|
|
4100
|
-
}
|
|
4101
|
-
} else {
|
|
4102
|
-
this._pendingMediaUrls.delete(sessionKey); // clean up either way
|
|
4103
|
-
}
|
|
4104
|
-
|
|
4105
4068
|
// Check for pending activity message
|
|
4106
4069
|
const pendingMsg = db.prepare(`
|
|
4107
4070
|
SELECT id, metadata FROM messages
|
|
@@ -4121,15 +4084,13 @@ export function createApp(config = {}) {
|
|
|
4121
4084
|
if (lastAssistantIdx >= 0) metadata.activityLog.splice(lastAssistantIdx, 1);
|
|
4122
4085
|
metadata.activitySummary = this.generateActivitySummary(metadata.activityLog);
|
|
4123
4086
|
}
|
|
4124
|
-
if (attachments) metadata.attachments = attachments;
|
|
4125
4087
|
db.prepare('UPDATE messages SET content = ?, metadata = ?, timestamp = ? WHERE id = ?')
|
|
4126
4088
|
.run(content, JSON.stringify(metadata), now, pendingMsg.id);
|
|
4127
4089
|
messageId = pendingMsg.id;
|
|
4128
4090
|
} else {
|
|
4129
4091
|
// No pending activity — normal INSERT (simple responses, no tools)
|
|
4130
4092
|
messageId = seq != null ? `gw-${parsed.threadId}-${seq}` : `gw-${parsed.threadId}-${now}`;
|
|
4131
|
-
|
|
4132
|
-
db.prepare(`INSERT INTO messages (id, thread_id, role, content, status, metadata, timestamp, created_at) VALUES (?, ?, 'assistant', ?, 'sent', ?, ?, ?) ON CONFLICT(id) DO UPDATE SET content = excluded.content, metadata = COALESCE(excluded.metadata, metadata), timestamp = excluded.timestamp`).run(messageId, parsed.threadId, content, metaStr, now, now);
|
|
4093
|
+
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);
|
|
4133
4094
|
}
|
|
4134
4095
|
|
|
4135
4096
|
try {
|
|
@@ -4171,8 +4132,6 @@ export function createApp(config = {}) {
|
|
|
4171
4132
|
} catch (e) { console.error(`Failed to save error marker:`, e.message); }
|
|
4172
4133
|
}
|
|
4173
4134
|
|
|
4174
|
-
// _scanRecentWorkspaceFiles removed — MEDIA: paths captured from delta stream instead
|
|
4175
|
-
|
|
4176
4135
|
generateThreadTitle(db, threadId, workspace, skipHeuristic = false) {
|
|
4177
4136
|
const thread = db.prepare('SELECT title FROM threads WHERE id = ?').get(threadId);
|
|
4178
4137
|
if (!thread) return;
|
|
@@ -4223,25 +4182,7 @@ export function createApp(config = {}) {
|
|
|
4223
4182
|
if (!this.activityLogs.has(runId)) this.activityLogs.set(runId, { sessionKey, steps: [], startTime: Date.now() });
|
|
4224
4183
|
const log = this.activityLogs.get(runId);
|
|
4225
4184
|
if (stream === 'assistant') {
|
|
4226
|
-
// Check data.mediaUrls — gateway populates this via splitMediaFromOutput on each streaming event.
|
|
4227
|
-
// Partial path events (e.g. ["/"]) fail the extension filter; complete-path events pass.
|
|
4228
|
-
const incomingUrls = Array.isArray(data?.mediaUrls) ? data.mediaUrls : (data?.mediaUrl ? [data.mediaUrl] : []);
|
|
4229
|
-
for (const p of incomingUrls) {
|
|
4230
|
-
if (typeof p === 'string' && /\.\w{1,10}$/.test(p)) {
|
|
4231
|
-
if (!log._seenMediaPaths) log._seenMediaPaths = new Set();
|
|
4232
|
-
if (!log._seenMediaPaths.has(p)) {
|
|
4233
|
-
log._seenMediaPaths.add(p);
|
|
4234
|
-
if (!this._pendingMediaUrls.has(sessionKey)) this._pendingMediaUrls.set(sessionKey, []);
|
|
4235
|
-
this._pendingMediaUrls.get(sessionKey).push(p);
|
|
4236
|
-
console.log('[ClawChats] MEDIA path captured from mediaUrls:', p);
|
|
4237
|
-
}
|
|
4238
|
-
}
|
|
4239
|
-
}
|
|
4240
|
-
if (incomingUrls.length > 0) console.log('[ClawChats] mediaUrls on event:', JSON.stringify(incomingUrls));
|
|
4241
4185
|
const text = data?.text || '';
|
|
4242
|
-
const delta = data?.delta || '';
|
|
4243
|
-
// Log if a MEDIA: directive appears in text or delta (must look like an actual path, not just the word)
|
|
4244
|
-
if (/MEDIA:\s*[./~a-zA-Z]/.test(text.slice(-80)) || /MEDIA:\s*[./~a-zA-Z]/.test(delta)) console.log('[ClawChats] MEDIA in stream! text tail:', JSON.stringify(text.slice(-80)), 'delta:', JSON.stringify(delta));
|
|
4245
4186
|
if (text) {
|
|
4246
4187
|
let currentSegment = log._currentAssistantSegment;
|
|
4247
4188
|
if (!currentSegment || currentSegment._sealed) {
|
|
@@ -4268,7 +4209,6 @@ export function createApp(config = {}) {
|
|
|
4268
4209
|
if (log._currentAssistantSegment && !log._currentAssistantSegment._sealed) { log._currentAssistantSegment._sealed = true; }
|
|
4269
4210
|
const step = { type: 'tool', timestamp: Date.now(), name: data?.name || 'unknown', phase: data?.phase || 'start', toolCallId: data?.toolCallId, meta: data?.meta, isError: data?.isError || false };
|
|
4270
4211
|
if (data?.phase === 'result') {
|
|
4271
|
-
if (data?.name === 'exec') console.log('[ClawChats] exec tool result meta:', JSON.stringify(data?.meta));
|
|
4272
4212
|
const existing = log.steps.findLast(s => s.toolCallId === data.toolCallId && (s.phase === 'start' || s.phase === 'running'));
|
|
4273
4213
|
if (existing) { existing.phase = 'done'; existing.resultMeta = data?.meta; existing.isError = data?.isError || false; existing.durationMs = Date.now() - existing.timestamp; }
|
|
4274
4214
|
else { step.phase = 'done'; log.steps.push(step); }
|
|
@@ -4281,7 +4221,6 @@ export function createApp(config = {}) {
|
|
|
4281
4221
|
}
|
|
4282
4222
|
if (stream === 'lifecycle') {
|
|
4283
4223
|
if (data?.phase === 'end' || data?.phase === 'error') {
|
|
4284
|
-
// MEDIA: paths already captured during delta streaming — nothing to do here
|
|
4285
4224
|
if (log._currentAssistantSegment && !log._currentAssistantSegment._sealed) log._currentAssistantSegment._sealed = true;
|
|
4286
4225
|
const lastAssistantIdx = log.steps.findLastIndex(s => s.type === 'assistant');
|
|
4287
4226
|
if (lastAssistantIdx >= 0) log.steps.splice(lastAssistantIdx, 1);
|
|
@@ -4608,7 +4547,7 @@ export function createApp(config = {}) {
|
|
|
4608
4547
|
}
|
|
4609
4548
|
|
|
4610
4549
|
// ── Browser WebSocket setup (shared logic for standalone and plugin) ────────
|
|
4611
|
-
|
|
4550
|
+
function _setupBrowserWs(wssInstance) {
|
|
4612
4551
|
wssInstance.on('connection', (ws) => {
|
|
4613
4552
|
console.log('Browser client connected');
|
|
4614
4553
|
_gatewayClient.addBrowserClient(ws);
|
|
@@ -4618,7 +4557,6 @@ export function createApp(config = {}) {
|
|
|
4618
4557
|
ws.on('message', (data) => {
|
|
4619
4558
|
const msgStr = data.toString();
|
|
4620
4559
|
_debugLogger.logFrame('BR→SRV', msgStr);
|
|
4621
|
-
let forwardStr = msgStr;
|
|
4622
4560
|
try {
|
|
4623
4561
|
const msg = JSON.parse(msgStr);
|
|
4624
4562
|
if (msg.type === 'req' && msg.method === 'connect') {
|
|
@@ -4638,9 +4576,8 @@ export function createApp(config = {}) {
|
|
|
4638
4576
|
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; }
|
|
4639
4577
|
if (msg.action === 'debug-dump') { const { sessionId, files } = _debugLogger.saveDump(msg); ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-saved', sessionId, files })); return; }
|
|
4640
4578
|
}
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
_gatewayClient.sendToGateway(forwardStr);
|
|
4579
|
+
} catch { /* Not JSON or not a ClawChats message, forward to gateway */ }
|
|
4580
|
+
_gatewayClient.sendToGateway(msgStr);
|
|
4644
4581
|
});
|
|
4645
4582
|
|
|
4646
4583
|
ws.on('close', () => { console.log('Browser client disconnected'); _debugLogger.handleClientDisconnect(ws); _gatewayClient.removeBrowserClient(ws); });
|