@clawchatsai/connector 0.0.74 → 0.0.76

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 (3) hide show
  1. package/dist/index.js +30 -0
  2. package/package.json +1 -1
  3. package/server.js +31 -2
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ let healthServer = null;
39
39
  let _stopRequested = false;
40
40
  /** Model IDs that have 'input' explicitly set without 'image' support. */
41
41
  let _imageRestrictedModels = [];
42
+ let _uploadsDir = null;
42
43
  // ---------------------------------------------------------------------------
43
44
  // Config helpers
44
45
  // ---------------------------------------------------------------------------
@@ -220,6 +221,7 @@ async function startClawChats(ctx, api, mediaStash) {
220
221
  // 4. Import server.js and create app instance with plugin paths
221
222
  const dataDir = path.join(ctx.stateDir, 'clawchats', 'data');
222
223
  const uploadsDir = path.join(ctx.stateDir, 'clawchats', 'uploads');
224
+ _uploadsDir = uploadsDir;
223
225
  // Dynamic import of server.js (plain JS, no type declarations)
224
226
  // @ts-expect-error — server.js is plain JS with no .d.ts
225
227
  const serverModule = await import('../server.js');
@@ -394,6 +396,34 @@ function normalizeGatewayPayload(raw) {
394
396
  try {
395
397
  const parsed = JSON.parse(raw);
396
398
  if (parsed.method === 'chat.send' && typeof parsed.params?.message === 'string') {
399
+ // Save inline base64 attachments to disk so the agent can reference them as file paths.
400
+ if (Array.isArray(parsed.params?.attachments) && parsed.params.attachments.length > 0 && _uploadsDir) {
401
+ const skMatch = (parsed.params.sessionKey || '').match(/^agent:[^:]+:[^:]+:chat:([^:]+)$/);
402
+ const threadId = skMatch?.[1] || 'misc';
403
+ const uploadDir = path.join(_uploadsDir, threadId);
404
+ const extMap = { jpeg: 'jpg', jpg: 'jpg', png: 'png', gif: 'gif', webp: 'webp', pdf: 'pdf', 'svg+xml': 'svg', mp3: 'mp3', mp4: 'mp4', wav: 'wav', webm: 'webm' };
405
+ const savedPaths = [];
406
+ for (const att of parsed.params.attachments) {
407
+ if (!att.content || !att.mimeType)
408
+ continue;
409
+ try {
410
+ const rawExt = att.mimeType.split('/')[1]?.split(';')[0] || 'bin';
411
+ const ext = extMap[rawExt] || rawExt;
412
+ const fileId = `${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
413
+ const filePath = path.join(uploadDir, `${fileId}.${ext}`);
414
+ fs.mkdirSync(uploadDir, { recursive: true });
415
+ fs.writeFileSync(filePath, Buffer.from(att.content, 'base64'));
416
+ savedPaths.push(filePath);
417
+ }
418
+ catch { /* skip attachment on error */ }
419
+ }
420
+ if (savedPaths.length > 0) {
421
+ const label = savedPaths.length === 1 ? 'Attached file saved on disk' : 'Attached files saved on disk';
422
+ parsed.params.message = parsed.params.message.trimEnd() +
423
+ `\n\n[${label}:\n${savedPaths.map((p) => `- ${p}`).join('\n')}]`;
424
+ return JSON.stringify(parsed);
425
+ }
426
+ }
397
427
  // Warn user in-chat if they're sending image attachments to an image-restricted model.
398
428
  // The warning is appended to the message so the AI echoes it back — impossible to miss.
399
429
  if (Array.isArray(parsed.params?.attachments) &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawchatsai/connector",
3
- "version": "0.0.74",
3
+ "version": "0.0.76",
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
@@ -4793,6 +4793,7 @@ export function createApp(config = {}) {
4793
4793
  ws.on('message', (data) => {
4794
4794
  const msgStr = data.toString();
4795
4795
  _debugLogger.logFrame('BR→SRV', msgStr);
4796
+ let msgToForward = msgStr;
4796
4797
  try {
4797
4798
  const msg = JSON.parse(msgStr);
4798
4799
  if (msg.type === 'req' && msg.method === 'connect') {
@@ -4812,8 +4813,36 @@ export function createApp(config = {}) {
4812
4813
  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; }
4813
4814
  if (msg.action === 'debug-dump') { const { sessionId, files } = _debugLogger.saveDump(msg); ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-saved', sessionId, files })); return; }
4814
4815
  }
4815
- } catch { /* Not JSON or not a ClawChats message, forward to gateway */ }
4816
- _gatewayClient.sendToGateway(msgStr);
4816
+ // Save inline attachments to disk so the agent can access them as file paths
4817
+ if (msg.type === 'req' && msg.method === 'chat.send' && msg.params?.attachments?.length > 0) {
4818
+ const parsed = parseSessionKey(msg.params.sessionKey || '');
4819
+ const threadId = parsed?.threadId || 'misc';
4820
+ const uploadDir = path.join(_UPLOADS_DIR, threadId);
4821
+ fs.mkdirSync(uploadDir, { recursive: true });
4822
+ const extMap = { jpeg: 'jpg', jpg: 'jpg', png: 'png', gif: 'gif', webp: 'webp', pdf: 'pdf', 'svg+xml': 'svg', mp3: 'mp3', mp4: 'mp4', wav: 'wav', webm: 'webm' };
4823
+ const savedPaths = [];
4824
+ for (const att of msg.params.attachments) {
4825
+ if (!att.content || !att.mimeType) continue;
4826
+ try {
4827
+ const rawExt = att.mimeType.split('/')[1]?.split(';')[0] || 'bin';
4828
+ const ext = extMap[rawExt] || rawExt;
4829
+ const fileId = `${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
4830
+ const filePath = path.join(uploadDir, `${fileId}.${ext}`);
4831
+ fs.writeFileSync(filePath, Buffer.from(att.content, 'base64'));
4832
+ savedPaths.push(filePath);
4833
+ console.log(`[upload] Saved attachment to ${filePath}`);
4834
+ } catch (err) {
4835
+ console.error('[upload] Failed to save attachment:', err.message);
4836
+ }
4837
+ }
4838
+ if (savedPaths.length > 0) {
4839
+ const label = savedPaths.length === 1 ? 'Attached file saved on disk' : 'Attached files saved on disk';
4840
+ const note = `\n\n[${label}:\n${savedPaths.map(p => `- ${p}`).join('\n')}]`;
4841
+ msgToForward = JSON.stringify({ ...msg, params: { ...msg.params, message: (msg.params.message || '') + note } });
4842
+ }
4843
+ }
4844
+ } catch { /* Not JSON or not a ClawChats message, forward as-is */ }
4845
+ _gatewayClient.sendToGateway(msgToForward);
4817
4846
  });
4818
4847
 
4819
4848
  ws.on('close', () => { console.log('Browser client disconnected'); _debugLogger.handleClientDisconnect(ws); _gatewayClient.removeBrowserClient(ws); });