gopherhole_openclaw_a2a 0.3.9 → 0.3.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.
@@ -16,6 +16,11 @@ export declare class A2AConnectionManager {
16
16
  start(): Promise<void>;
17
17
  private connectToGopherHole;
18
18
  private handleIncomingMessage;
19
+ /**
20
+ * Handle system messages from GopherHole Hub
21
+ * These include spending alerts, account alerts, notices, maintenance, etc.
22
+ */
23
+ private handleSystemMessage;
19
24
  stop(): Promise<void>;
20
25
  /**
21
26
  * Send a message to another agent via GopherHole and wait for response
@@ -49,15 +54,38 @@ export declare class A2AConnectionManager {
49
54
  */
50
55
  isGopherHoleConnected(): boolean;
51
56
  /**
52
- * List available agents from GopherHole
53
- * Fetches same-tenant agents + agents with approved access + public agents
57
+ * Make an A2A JSON-RPC call
54
58
  */
55
- listAvailableAgents(): Promise<Array<{
59
+ private a2aRpc;
60
+ /**
61
+ * List available agents from GopherHole (agents you have access to)
62
+ */
63
+ listAvailableAgents(options?: {
64
+ query?: string;
65
+ public?: boolean;
66
+ }): Promise<Array<{
56
67
  id: string;
57
68
  name: string;
58
69
  description?: string;
70
+ verified?: boolean;
59
71
  accessType: 'same-tenant' | 'public' | 'granted';
60
72
  }>>;
73
+ /**
74
+ * Discover public agents in the marketplace
75
+ */
76
+ discoverAgents(options?: {
77
+ query?: string;
78
+ category?: string;
79
+ verified?: boolean;
80
+ limit?: number;
81
+ }): Promise<Array<{
82
+ id: string;
83
+ name: string;
84
+ description?: string;
85
+ verified?: boolean;
86
+ tenantName?: string;
87
+ avgRating?: number;
88
+ }>>;
61
89
  /**
62
90
  * List connection status (for backward compatibility)
63
91
  */
@@ -92,6 +92,10 @@ export class A2AConnectionManager {
92
92
  this.gopherhole.on('message', (message) => {
93
93
  this.handleIncomingMessage(message);
94
94
  });
95
+ // Handle system messages (rate limits, budget alerts, announcements)
96
+ this.gopherhole.on('system', (message) => {
97
+ this.handleSystemMessage(message);
98
+ });
95
99
  // Connect
96
100
  try {
97
101
  await this.gopherhole.connect();
@@ -131,6 +135,40 @@ export class A2AConnectionManager {
131
135
  console.error('[a2a] Error handling incoming message:', err);
132
136
  });
133
137
  }
138
+ /**
139
+ * Handle system messages from GopherHole Hub
140
+ * These include spending alerts, account alerts, notices, maintenance, etc.
141
+ */
142
+ handleSystemMessage(message) {
143
+ const kind = message.metadata?.kind;
144
+ const text = message.payload.parts.find(p => p.kind === 'text')?.text || '';
145
+ const data = message.metadata?.data;
146
+ // Log all system messages
147
+ console.log(`[a2a] System message (${kind || 'unknown'}): ${text}`);
148
+ // Handle specific message kinds
149
+ switch (kind) {
150
+ case 'spending_alert':
151
+ console.warn(`[a2a] 💰 Spending alert: ${text}`);
152
+ if (data) {
153
+ console.warn(`[a2a] Spending data:`, JSON.stringify(data));
154
+ }
155
+ break;
156
+ case 'account_alert':
157
+ console.warn(`[a2a] ⚠️ Account alert: ${text}`);
158
+ break;
159
+ case 'system_notice':
160
+ console.log(`[a2a] 📢 System notice: ${text}`);
161
+ break;
162
+ case 'maintenance':
163
+ console.warn(`[a2a] 🔧 Maintenance notice: ${text}`);
164
+ break;
165
+ default:
166
+ // Log but don't warn for unknown types
167
+ if (kind) {
168
+ console.log(`[a2a] System message "${kind}": ${text}`);
169
+ }
170
+ }
171
+ }
134
172
  async stop() {
135
173
  if (this.gopherhole) {
136
174
  this.gopherhole.disconnect();
@@ -234,39 +272,66 @@ export class A2AConnectionManager {
234
272
  return this.connected && this.gopherhole?.connected === true;
235
273
  }
236
274
  /**
237
- * List available agents from GopherHole
238
- * Fetches same-tenant agents + agents with approved access + public agents
275
+ * Make an A2A JSON-RPC call
239
276
  */
240
- async listAvailableAgents() {
277
+ async a2aRpc(method, params) {
241
278
  if (!this.config.apiKey) {
242
- return [];
279
+ return null;
243
280
  }
244
281
  const hubUrl = this.config.bridgeUrl || 'wss://hub.gopherhole.ai/ws';
245
- // Convert wss:// to https:// for API calls
246
282
  const apiBase = hubUrl.replace('wss://', 'https://').replace('/ws', '');
247
283
  try {
248
- const response = await fetch(`${apiBase}/api/agents/available`, {
284
+ const response = await fetch(`${apiBase}/a2a`, {
285
+ method: 'POST',
249
286
  headers: {
250
287
  'Authorization': `Bearer ${this.config.apiKey}`,
251
288
  'Content-Type': 'application/json',
252
289
  },
290
+ body: JSON.stringify({
291
+ jsonrpc: '2.0',
292
+ method,
293
+ params: params || {},
294
+ id: Date.now(),
295
+ }),
253
296
  });
254
297
  if (!response.ok) {
255
- console.error(`[a2a] Failed to fetch agents: ${response.status}`);
256
- return [];
298
+ console.error(`[a2a] RPC failed: ${response.status}`);
299
+ return null;
257
300
  }
258
301
  const data = await response.json();
259
- return data.agents.map(a => ({
260
- id: a.id,
261
- name: a.name,
262
- description: a.description,
263
- accessType: a.access_type,
264
- }));
302
+ if (data.error) {
303
+ console.error(`[a2a] RPC error: ${data.error.message}`);
304
+ return null;
305
+ }
306
+ return data.result || null;
265
307
  }
266
308
  catch (err) {
267
- console.error('[a2a] Error fetching available agents:', err.message);
309
+ console.error('[a2a] RPC error:', err.message);
310
+ return null;
311
+ }
312
+ }
313
+ /**
314
+ * List available agents from GopherHole (agents you have access to)
315
+ */
316
+ async listAvailableAgents(options) {
317
+ const result = await this.a2aRpc('x-gopherhole/agents.available', options);
318
+ if (!result?.agents) {
268
319
  return [];
269
320
  }
321
+ return result.agents.map(a => ({
322
+ id: a.id,
323
+ name: a.name,
324
+ description: a.description,
325
+ verified: a.verified,
326
+ accessType: a.accessType,
327
+ }));
328
+ }
329
+ /**
330
+ * Discover public agents in the marketplace
331
+ */
332
+ async discoverAgents(options) {
333
+ const result = await this.a2aRpc('x-gopherhole/agents.discover', options);
334
+ return result?.agents || [];
270
335
  }
271
336
  /**
272
337
  * List connection status (for backward compatibility)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gopherhole_openclaw_a2a",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
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",
@@ -35,6 +35,7 @@
35
35
  "uuid": "^10.0.0"
36
36
  },
37
37
  "devDependencies": {
38
+ "@types/node": "^25.5.0",
38
39
  "@types/uuid": "^10.0.0",
39
40
  "typescript": "^5.9.3"
40
41
  },
package/src/connection.ts CHANGED
@@ -113,6 +113,11 @@ export class A2AConnectionManager {
113
113
  this.handleIncomingMessage(message);
114
114
  });
115
115
 
116
+ // Handle system messages (rate limits, budget alerts, announcements)
117
+ this.gopherhole.on('system', (message) => {
118
+ this.handleSystemMessage(message);
119
+ });
120
+
116
121
  // Connect
117
122
  try {
118
123
  await this.gopherhole.connect();
@@ -157,6 +162,47 @@ export class A2AConnectionManager {
157
162
  });
158
163
  }
159
164
 
165
+ /**
166
+ * Handle system messages from GopherHole Hub
167
+ * These include spending alerts, account alerts, notices, maintenance, etc.
168
+ */
169
+ private handleSystemMessage(message: Message): void {
170
+ const kind = message.metadata?.kind;
171
+ const text = message.payload.parts.find(p => p.kind === 'text')?.text || '';
172
+ const data = message.metadata?.data;
173
+
174
+ // Log all system messages
175
+ console.log(`[a2a] System message (${kind || 'unknown'}): ${text}`);
176
+
177
+ // Handle specific message kinds
178
+ switch (kind) {
179
+ case 'spending_alert':
180
+ console.warn(`[a2a] 💰 Spending alert: ${text}`);
181
+ if (data) {
182
+ console.warn(`[a2a] Spending data:`, JSON.stringify(data));
183
+ }
184
+ break;
185
+
186
+ case 'account_alert':
187
+ console.warn(`[a2a] ⚠️ Account alert: ${text}`);
188
+ break;
189
+
190
+ case 'system_notice':
191
+ console.log(`[a2a] 📢 System notice: ${text}`);
192
+ break;
193
+
194
+ case 'maintenance':
195
+ console.warn(`[a2a] 🔧 Maintenance notice: ${text}`);
196
+ break;
197
+
198
+ default:
199
+ // Log but don't warn for unknown types
200
+ if (kind) {
201
+ console.log(`[a2a] System message "${kind}": ${text}`);
202
+ }
203
+ }
204
+ }
205
+
160
206
  async stop(): Promise<void> {
161
207
  if (this.gopherhole) {
162
208
  this.gopherhole.disconnect();
@@ -297,53 +343,106 @@ export class A2AConnectionManager {
297
343
  }
298
344
 
299
345
  /**
300
- * List available agents from GopherHole
301
- * Fetches same-tenant agents + agents with approved access + public agents
346
+ * Make an A2A JSON-RPC call
302
347
  */
303
- async listAvailableAgents(): Promise<Array<{
304
- id: string;
305
- name: string;
306
- description?: string;
307
- accessType: 'same-tenant' | 'public' | 'granted';
308
- }>> {
348
+ private async a2aRpc<T>(method: string, params?: Record<string, unknown>): Promise<T | null> {
309
349
  if (!this.config.apiKey) {
310
- return [];
350
+ return null;
311
351
  }
312
352
 
313
353
  const hubUrl = this.config.bridgeUrl || 'wss://hub.gopherhole.ai/ws';
314
- // Convert wss:// to https:// for API calls
315
354
  const apiBase = hubUrl.replace('wss://', 'https://').replace('/ws', '');
316
355
 
317
356
  try {
318
- const response = await fetch(`${apiBase}/api/agents/available`, {
357
+ const response = await fetch(`${apiBase}/a2a`, {
358
+ method: 'POST',
319
359
  headers: {
320
360
  'Authorization': `Bearer ${this.config.apiKey}`,
321
361
  'Content-Type': 'application/json',
322
362
  },
363
+ body: JSON.stringify({
364
+ jsonrpc: '2.0',
365
+ method,
366
+ params: params || {},
367
+ id: Date.now(),
368
+ }),
323
369
  });
324
370
 
325
371
  if (!response.ok) {
326
- console.error(`[a2a] Failed to fetch agents: ${response.status}`);
327
- return [];
372
+ console.error(`[a2a] RPC failed: ${response.status}`);
373
+ return null;
328
374
  }
329
375
 
330
- const data = await response.json() as { agents: Array<{
331
- id: string;
332
- name: string;
333
- description?: string;
334
- access_type: string;
335
- }> };
336
-
337
- return data.agents.map(a => ({
338
- id: a.id,
339
- name: a.name,
340
- description: a.description,
341
- accessType: a.access_type as 'same-tenant' | 'public' | 'granted',
342
- }));
376
+ const data = await response.json() as { result?: T; error?: { message: string } };
377
+ if (data.error) {
378
+ console.error(`[a2a] RPC error: ${data.error.message}`);
379
+ return null;
380
+ }
381
+
382
+ return data.result || null;
343
383
  } catch (err) {
344
- console.error('[a2a] Error fetching available agents:', (err as Error).message);
384
+ console.error('[a2a] RPC error:', (err as Error).message);
385
+ return null;
386
+ }
387
+ }
388
+
389
+ /**
390
+ * List available agents from GopherHole (agents you have access to)
391
+ */
392
+ async listAvailableAgents(options?: { query?: string; public?: boolean }): Promise<Array<{
393
+ id: string;
394
+ name: string;
395
+ description?: string;
396
+ verified?: boolean;
397
+ accessType: 'same-tenant' | 'public' | 'granted';
398
+ }>> {
399
+ const result = await this.a2aRpc<{ agents: Array<{
400
+ id: string;
401
+ name: string;
402
+ description?: string;
403
+ verified?: boolean;
404
+ accessType: string;
405
+ }> }>('x-gopherhole/agents.available', options);
406
+
407
+ if (!result?.agents) {
345
408
  return [];
346
409
  }
410
+
411
+ return result.agents.map(a => ({
412
+ id: a.id,
413
+ name: a.name,
414
+ description: a.description,
415
+ verified: a.verified,
416
+ accessType: a.accessType as 'same-tenant' | 'public' | 'granted',
417
+ }));
418
+ }
419
+
420
+ /**
421
+ * Discover public agents in the marketplace
422
+ */
423
+ async discoverAgents(options?: {
424
+ query?: string;
425
+ category?: string;
426
+ verified?: boolean;
427
+ limit?: number;
428
+ }): Promise<Array<{
429
+ id: string;
430
+ name: string;
431
+ description?: string;
432
+ verified?: boolean;
433
+ tenantName?: string;
434
+ avgRating?: number;
435
+ }>> {
436
+ const result = await this.a2aRpc<{ agents: Array<{
437
+ id: string;
438
+ name: string;
439
+ description?: string;
440
+ verified?: boolean;
441
+ tenantName?: string;
442
+ avgRating?: number;
443
+ }> }>('x-gopherhole/agents.discover', options);
444
+
445
+ return result?.agents || [];
347
446
  }
348
447
 
349
448
  /**