gopherhole_openclaw_a2a 0.2.0 → 0.2.2

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.js CHANGED
@@ -37,6 +37,10 @@ const plugin = {
37
37
  type: 'string',
38
38
  description: 'Path to image file to send (for send action)',
39
39
  },
40
+ file: {
41
+ type: 'string',
42
+ description: 'Path to file to send - PDF, documents, etc. (for send action)',
43
+ },
40
44
  },
41
45
  required: ['action'],
42
46
  },
@@ -45,6 +49,7 @@ const plugin = {
45
49
  const agentId = params.agentId;
46
50
  const message = params.message;
47
51
  const imagePath = params.image;
52
+ const filePath = params.file;
48
53
  const manager = getA2AConnectionManager();
49
54
  if (!manager) {
50
55
  return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'A2A channel not running' }) }] };
@@ -54,8 +59,8 @@ const plugin = {
54
59
  return { content: [{ type: 'text', text: JSON.stringify({ status: 'ok', agents }) }] };
55
60
  }
56
61
  if (action === 'send') {
57
- if (!agentId || (!message && !imagePath)) {
58
- return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'agentId and (message or image) required for send action' }) }] };
62
+ if (!agentId || (!message && !imagePath && !filePath)) {
63
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'agentId and (message, image, or file) required for send action' }) }] };
59
64
  }
60
65
  try {
61
66
  const isGopherHoleConnected = manager.isGopherHoleConnected();
@@ -66,25 +71,43 @@ const plugin = {
66
71
  if (message) {
67
72
  parts.push({ kind: 'text', text: message });
68
73
  }
69
- // Add image part if image path provided
70
- if (imagePath) {
74
+ // Add file part if image or file path provided
75
+ const attachmentPath = imagePath || filePath;
76
+ if (attachmentPath) {
71
77
  try {
72
- const imageData = readFileSync(imagePath);
73
- const base64Data = imageData.toString('base64');
74
- const ext = extname(imagePath).toLowerCase();
78
+ const fileData = readFileSync(attachmentPath);
79
+ const base64Data = fileData.toString('base64');
80
+ const ext = extname(attachmentPath).toLowerCase();
75
81
  const mimeTypes = {
82
+ // Images
76
83
  '.png': 'image/png',
77
84
  '.jpg': 'image/jpeg',
78
85
  '.jpeg': 'image/jpeg',
79
86
  '.gif': 'image/gif',
80
87
  '.webp': 'image/webp',
81
88
  '.svg': 'image/svg+xml',
89
+ // Documents
90
+ '.pdf': 'application/pdf',
91
+ '.doc': 'application/msword',
92
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
93
+ '.xls': 'application/vnd.ms-excel',
94
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
95
+ '.ppt': 'application/vnd.ms-powerpoint',
96
+ '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
97
+ '.txt': 'text/plain',
98
+ '.csv': 'text/csv',
99
+ '.json': 'application/json',
100
+ '.xml': 'application/xml',
101
+ '.html': 'text/html',
102
+ '.md': 'text/markdown',
103
+ // Archives
104
+ '.zip': 'application/zip',
82
105
  };
83
106
  const mimeType = mimeTypes[ext] || 'application/octet-stream';
84
- parts.push({ kind: 'image', data: base64Data, mimeType });
107
+ parts.push({ kind: 'data', data: base64Data, mimeType });
85
108
  }
86
- catch (imgErr) {
87
- return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: `Failed to read image: ${imgErr.message}` }) }] };
109
+ catch (fileErr) {
110
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: `Failed to read file: ${fileErr.message}` }) }] };
88
111
  }
89
112
  }
90
113
  let response;
package/index.ts CHANGED
@@ -52,6 +52,10 @@ const plugin = {
52
52
  type: 'string',
53
53
  description: 'Path to image file to send (for send action)',
54
54
  },
55
+ file: {
56
+ type: 'string',
57
+ description: 'Path to file to send - PDF, documents, etc. (for send action)',
58
+ },
55
59
  },
56
60
  required: ['action'],
57
61
  },
@@ -60,6 +64,7 @@ const plugin = {
60
64
  const agentId = params.agentId as string | undefined;
61
65
  const message = params.message as string | undefined;
62
66
  const imagePath = params.image as string | undefined;
67
+ const filePath = params.file as string | undefined;
63
68
 
64
69
  const manager = getA2AConnectionManager();
65
70
  if (!manager) {
@@ -72,8 +77,8 @@ const plugin = {
72
77
  }
73
78
 
74
79
  if (action === 'send') {
75
- if (!agentId || (!message && !imagePath)) {
76
- return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'agentId and (message or image) required for send action' }) }] };
80
+ if (!agentId || (!message && !imagePath && !filePath)) {
81
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: 'agentId and (message, image, or file) required for send action' }) }] };
77
82
  }
78
83
  try {
79
84
  const isGopherHoleConnected = manager.isGopherHoleConnected();
@@ -87,24 +92,42 @@ const plugin = {
87
92
  parts.push({ kind: 'text', text: message });
88
93
  }
89
94
 
90
- // Add image part if image path provided
91
- if (imagePath) {
95
+ // Add file part if image or file path provided
96
+ const attachmentPath = imagePath || filePath;
97
+ if (attachmentPath) {
92
98
  try {
93
- const imageData = readFileSync(imagePath);
94
- const base64Data = imageData.toString('base64');
95
- const ext = extname(imagePath).toLowerCase();
99
+ const fileData = readFileSync(attachmentPath);
100
+ const base64Data = fileData.toString('base64');
101
+ const ext = extname(attachmentPath).toLowerCase();
96
102
  const mimeTypes: Record<string, string> = {
103
+ // Images
97
104
  '.png': 'image/png',
98
105
  '.jpg': 'image/jpeg',
99
106
  '.jpeg': 'image/jpeg',
100
107
  '.gif': 'image/gif',
101
108
  '.webp': 'image/webp',
102
109
  '.svg': 'image/svg+xml',
110
+ // Documents
111
+ '.pdf': 'application/pdf',
112
+ '.doc': 'application/msword',
113
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
114
+ '.xls': 'application/vnd.ms-excel',
115
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
116
+ '.ppt': 'application/vnd.ms-powerpoint',
117
+ '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
118
+ '.txt': 'text/plain',
119
+ '.csv': 'text/csv',
120
+ '.json': 'application/json',
121
+ '.xml': 'application/xml',
122
+ '.html': 'text/html',
123
+ '.md': 'text/markdown',
124
+ // Archives
125
+ '.zip': 'application/zip',
103
126
  };
104
127
  const mimeType = mimeTypes[ext] || 'application/octet-stream';
105
- parts.push({ kind: 'image', data: base64Data, mimeType });
106
- } catch (imgErr) {
107
- return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: `Failed to read image: ${(imgErr as Error).message}` }) }] };
128
+ parts.push({ kind: 'data', data: base64Data, mimeType });
129
+ } catch (fileErr) {
130
+ return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: `Failed to read file: ${(fileErr as Error).message}` }) }] };
108
131
  }
109
132
  }
110
133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gopherhole_openclaw_a2a",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "GopherHole A2A plugin for OpenClaw - connect your AI agent to the GopherHole network",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/test-image.mjs ADDED
@@ -0,0 +1,29 @@
1
+ import { GopherHole } from '@gopherhole/sdk';
2
+ import { readFileSync } from 'fs';
3
+
4
+ const gph = new GopherHole({
5
+ apiKey: 'gph_a3ed7c3f30e5415e9dc92b72c1c05b78',
6
+ hubUrl: 'wss://gopherhole.ai/ws',
7
+ });
8
+
9
+ await gph.connect();
10
+ console.log('Connected, sending image...');
11
+
12
+ const imagePath = '/Users/brettwaterson/.marketclaw/images/1771822365256-A3kAAzoE.jpg';
13
+ const imageData = readFileSync(imagePath).toString('base64');
14
+ console.log('Image size:', imageData.length, 'chars');
15
+
16
+ const task = await gph.send('agent-70153299', {
17
+ role: 'agent',
18
+ parts: [
19
+ { kind: 'text', text: 'What do you see in this image? Please describe it.' },
20
+ { kind: 'data', mimeType: 'image/jpeg', data: imageData },
21
+ ],
22
+ });
23
+
24
+ console.log('Task created:', task.id);
25
+
26
+ const completed = await gph.waitForTask(task.id, { maxWaitMs: 60000 });
27
+ console.log('Response:', completed.artifacts?.[0]?.parts?.[0]?.text || JSON.stringify(completed));
28
+
29
+ gph.disconnect();
@@ -0,0 +1,37 @@
1
+ import { GopherHole } from '@gopherhole/sdk';
2
+ import { readFileSync } from 'fs';
3
+
4
+ const gph = new GopherHole({
5
+ apiKey: 'gph_a3ed7c3f30e5415e9dc92b72c1c05b78',
6
+ hubUrl: 'wss://gopherhole.ai/ws',
7
+ });
8
+
9
+ await gph.connect();
10
+ console.log('Connected as:', gph.id);
11
+
12
+ const imagePath = '/Users/brettwaterson/.marketclaw/images/1771822365256-A3kAAzoE.jpg';
13
+ const imageData = readFileSync(imagePath).toString('base64');
14
+ console.log('Image size:', imageData.length, 'chars (base64)');
15
+
16
+ const payload = {
17
+ role: 'agent',
18
+ parts: [
19
+ { kind: 'text', text: 'Describe this image please.' },
20
+ { kind: 'data', mimeType: 'image/jpeg', data: imageData },
21
+ ],
22
+ };
23
+
24
+ console.log('Sending payload with', payload.parts.length, 'parts');
25
+ console.log('Part 0:', { kind: payload.parts[0].kind, hasText: !!payload.parts[0].text });
26
+ console.log('Part 1:', { kind: payload.parts[1].kind, mimeType: payload.parts[1].mimeType, dataLen: payload.parts[1].data?.length });
27
+
28
+ const task = await gph.send('agent-70153299', payload);
29
+ console.log('Task:', task.id, 'Status:', task.status?.state);
30
+
31
+ const completed = await gph.waitForTask(task.id, { maxWaitMs: 60000 });
32
+ console.log('Final status:', completed.status?.state);
33
+
34
+ const response = completed.artifacts?.[0]?.parts?.[0]?.text;
35
+ console.log('Response:', response?.slice(0, 300) || 'No text in response');
36
+
37
+ gph.disconnect();