@gopherhole/sdk 0.1.0 → 0.1.1
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/README.md +140 -73
- package/dist/index.d.mts +499 -0
- package/dist/index.d.ts +472 -40
- package/dist/index.js +442 -197
- package/dist/index.mjs +433 -0
- package/package.json +39 -7
- package/src/index.ts +607 -204
- package/src/types.ts +232 -0
- package/tsconfig.json +9 -6
- package/listen-marketclaw.ts +0 -26
- package/send-from-nova.ts +0 -28
- package/test.ts +0 -86
package/dist/index.js
CHANGED
|
@@ -1,207 +1,452 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
GopherHole: () => GopherHole,
|
|
24
|
+
JsonRpcErrorCodes: () => JsonRpcErrorCodes,
|
|
25
|
+
default: () => index_default
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var import_eventemitter3 = require("eventemitter3");
|
|
29
|
+
|
|
30
|
+
// src/types.ts
|
|
31
|
+
var JsonRpcErrorCodes = {
|
|
32
|
+
ParseError: -32700,
|
|
33
|
+
InvalidRequest: -32600,
|
|
34
|
+
MethodNotFound: -32601,
|
|
35
|
+
InvalidParams: -32602,
|
|
36
|
+
InternalError: -32603,
|
|
37
|
+
// A2A-specific errors
|
|
38
|
+
TaskNotFound: -32001,
|
|
39
|
+
TaskNotCancelable: -32002,
|
|
40
|
+
PushNotificationNotSupported: -32003,
|
|
41
|
+
UnsupportedOperation: -32004,
|
|
42
|
+
ContentTypeNotSupported: -32005,
|
|
43
|
+
InvalidAgentCard: -32006
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/index.ts
|
|
47
|
+
var DEFAULT_HUB_URL = "wss://gopherhole.helixdata.workers.dev/ws";
|
|
48
|
+
var GopherHole = class extends import_eventemitter3.EventEmitter {
|
|
49
|
+
constructor(apiKeyOrOptions) {
|
|
50
|
+
super();
|
|
51
|
+
this.ws = null;
|
|
52
|
+
this.reconnectAttempts = 0;
|
|
53
|
+
this.reconnectTimer = null;
|
|
54
|
+
this.pingInterval = null;
|
|
55
|
+
this.agentId = null;
|
|
56
|
+
this.agentCard = null;
|
|
57
|
+
const options = typeof apiKeyOrOptions === "string" ? { apiKey: apiKeyOrOptions } : apiKeyOrOptions;
|
|
58
|
+
this.apiKey = options.apiKey;
|
|
59
|
+
this.hubUrl = options.hubUrl || DEFAULT_HUB_URL;
|
|
60
|
+
this.apiUrl = this.hubUrl.replace("/ws", "").replace("wss://", "https://").replace("ws://", "http://");
|
|
61
|
+
this.agentCard = options.agentCard || null;
|
|
62
|
+
this.autoReconnect = options.autoReconnect ?? true;
|
|
63
|
+
this.reconnectDelay = options.reconnectDelay ?? 1e3;
|
|
64
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 10;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Update agent card (sends to hub if connected)
|
|
68
|
+
*/
|
|
69
|
+
async updateCard(card) {
|
|
70
|
+
this.agentCard = card;
|
|
71
|
+
if (this.ws?.readyState === 1) {
|
|
72
|
+
this.ws.send(JSON.stringify({ type: "update_card", agentCard: card }));
|
|
19
73
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const msg = JSON.parse(data.toString());
|
|
31
|
-
this.handleMessage(msg, resolve, reject);
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
console.error('[GopherHole] Failed to parse message:', err);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
this.ws.on('close', () => {
|
|
38
|
-
console.log('[GopherHole] Disconnected');
|
|
39
|
-
this.authenticated = false;
|
|
40
|
-
this.emit('disconnect');
|
|
41
|
-
if (this.reconnect) {
|
|
42
|
-
console.log(`[GopherHole] Reconnecting in ${this.reconnectDelay}ms...`);
|
|
43
|
-
setTimeout(() => this.connect(), this.reconnectDelay);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
this.ws.on('error', (err) => {
|
|
47
|
-
console.error('[GopherHole] WebSocket error:', err);
|
|
48
|
-
this.emit('error', err);
|
|
49
|
-
if (!this.authenticated) {
|
|
50
|
-
reject(err);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
reject(err);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
handleMessage(msg, resolve, reject) {
|
|
60
|
-
switch (msg.type) {
|
|
61
|
-
case 'auth_ok':
|
|
62
|
-
console.log(`[GopherHole] Authenticated as ${msg.agentId}`);
|
|
63
|
-
this.authenticated = true;
|
|
64
|
-
this.agentId = msg.agentId || null;
|
|
65
|
-
this.emit('connected', { agentId: this.agentId });
|
|
66
|
-
resolve?.();
|
|
67
|
-
break;
|
|
68
|
-
case 'auth_error':
|
|
69
|
-
console.error('[GopherHole] Auth failed:', msg.error);
|
|
70
|
-
reject?.(new Error(msg.error || 'Authentication failed'));
|
|
71
|
-
break;
|
|
72
|
-
case 'message':
|
|
73
|
-
const incomingMsg = {
|
|
74
|
-
from: msg.from,
|
|
75
|
-
to: msg.to,
|
|
76
|
-
payload: msg.payload,
|
|
77
|
-
timestamp: msg.timestamp || Date.now(),
|
|
78
|
-
};
|
|
79
|
-
console.log(`[GopherHole] Message from ${msg.from}`);
|
|
80
|
-
this.emit('message', incomingMsg);
|
|
81
|
-
break;
|
|
82
|
-
case 'ack':
|
|
83
|
-
const pending = this.pendingMessages.get(msg.id);
|
|
84
|
-
if (pending) {
|
|
85
|
-
pending.resolve({ id: msg.id, timestamp: msg.timestamp });
|
|
86
|
-
this.pendingMessages.delete(msg.id);
|
|
87
|
-
}
|
|
88
|
-
break;
|
|
89
|
-
case 'error':
|
|
90
|
-
console.error('[GopherHole] Error:', msg.error);
|
|
91
|
-
const pendingErr = msg.id ? this.pendingMessages.get(msg.id) : null;
|
|
92
|
-
if (pendingErr) {
|
|
93
|
-
pendingErr.reject(new Error(msg.error));
|
|
94
|
-
this.pendingMessages.delete(msg.id);
|
|
95
|
-
}
|
|
96
|
-
this.emit('error', new Error(msg.error));
|
|
97
|
-
break;
|
|
98
|
-
default:
|
|
99
|
-
console.log('[GopherHole] Unknown message type:', msg.type);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Connect to the GopherHole hub via WebSocket
|
|
77
|
+
*/
|
|
78
|
+
async connect() {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const WS = typeof WebSocket !== "undefined" ? WebSocket : require("ws");
|
|
81
|
+
const ws = new WS(this.hubUrl, {
|
|
82
|
+
headers: {
|
|
83
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
100
84
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
});
|
|
86
|
+
this.ws = ws;
|
|
87
|
+
ws.onopen = () => {
|
|
88
|
+
this.reconnectAttempts = 0;
|
|
89
|
+
this.startPing();
|
|
90
|
+
this.emit("connect");
|
|
91
|
+
resolve();
|
|
92
|
+
};
|
|
93
|
+
ws.onclose = (event) => {
|
|
94
|
+
this.stopPing();
|
|
95
|
+
const reason = event.reason || "Connection closed";
|
|
96
|
+
this.emit("disconnect", reason);
|
|
97
|
+
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
98
|
+
this.scheduleReconnect();
|
|
105
99
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
100
|
+
};
|
|
101
|
+
ws.onerror = () => {
|
|
102
|
+
const error = new Error("WebSocket error");
|
|
103
|
+
this.emit("error", error);
|
|
104
|
+
reject(error);
|
|
105
|
+
};
|
|
106
|
+
ws.onmessage = (event) => {
|
|
107
|
+
try {
|
|
108
|
+
const data = JSON.parse(event.data.toString());
|
|
109
|
+
this.handleMessage(data);
|
|
110
|
+
} catch {
|
|
111
|
+
this.emit("error", new Error("Failed to parse message"));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Disconnect from the hub
|
|
118
|
+
*/
|
|
119
|
+
disconnect() {
|
|
120
|
+
this.autoReconnect = false;
|
|
121
|
+
this.stopPing();
|
|
122
|
+
if (this.reconnectTimer) {
|
|
123
|
+
clearTimeout(this.reconnectTimer);
|
|
124
|
+
this.reconnectTimer = null;
|
|
123
125
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
contextId,
|
|
128
|
-
});
|
|
126
|
+
if (this.ws) {
|
|
127
|
+
this.ws.close();
|
|
128
|
+
this.ws = null;
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Send a message to another agent
|
|
133
|
+
*/
|
|
134
|
+
async send(toAgentId, payload, options) {
|
|
135
|
+
const response = await this.rpc("message/send", {
|
|
136
|
+
message: payload,
|
|
137
|
+
configuration: {
|
|
138
|
+
agentId: toAgentId,
|
|
139
|
+
...options
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return response;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Send a text message to another agent
|
|
146
|
+
*/
|
|
147
|
+
async sendText(toAgentId, text, options) {
|
|
148
|
+
return this.send(toAgentId, {
|
|
149
|
+
role: "agent",
|
|
150
|
+
parts: [{ kind: "text", text }]
|
|
151
|
+
}, options);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get a task by ID
|
|
155
|
+
*/
|
|
156
|
+
async getTask(taskId, historyLength) {
|
|
157
|
+
const response = await this.rpc("tasks/get", {
|
|
158
|
+
id: taskId,
|
|
159
|
+
historyLength
|
|
160
|
+
});
|
|
161
|
+
return response;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* List tasks
|
|
165
|
+
*/
|
|
166
|
+
async listTasks(options) {
|
|
167
|
+
const response = await this.rpc("tasks/list", options || {});
|
|
168
|
+
return response;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Cancel a task
|
|
172
|
+
*/
|
|
173
|
+
async cancelTask(taskId) {
|
|
174
|
+
const response = await this.rpc("tasks/cancel", { id: taskId });
|
|
175
|
+
return response;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Reply to a message/task (sends back to the original caller)
|
|
179
|
+
*/
|
|
180
|
+
async reply(taskId, payload, toAgentId) {
|
|
181
|
+
if (!toAgentId) {
|
|
182
|
+
const task = await this.getTask(taskId);
|
|
183
|
+
const response2 = await this.rpc("message/send", {
|
|
184
|
+
message: payload,
|
|
185
|
+
configuration: {
|
|
186
|
+
contextId: task.contextId
|
|
187
|
+
// Server needs to handle replies via context routing
|
|
135
188
|
}
|
|
189
|
+
});
|
|
190
|
+
return response2;
|
|
136
191
|
}
|
|
137
|
-
|
|
138
|
-
|
|
192
|
+
const response = await this.rpc("message/send", {
|
|
193
|
+
message: payload,
|
|
194
|
+
configuration: {
|
|
195
|
+
agentId: toAgentId
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
return response;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Reply with text
|
|
202
|
+
*/
|
|
203
|
+
async replyText(taskId, text) {
|
|
204
|
+
return this.reply(taskId, {
|
|
205
|
+
role: "agent",
|
|
206
|
+
parts: [{ kind: "text", text }]
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Make a JSON-RPC call to the A2A endpoint
|
|
211
|
+
*/
|
|
212
|
+
async rpc(method, params) {
|
|
213
|
+
const response = await fetch(`${this.apiUrl}/a2a`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: {
|
|
216
|
+
"Content-Type": "application/json",
|
|
217
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
218
|
+
},
|
|
219
|
+
body: JSON.stringify({
|
|
220
|
+
jsonrpc: "2.0",
|
|
221
|
+
method,
|
|
222
|
+
params,
|
|
223
|
+
id: Date.now()
|
|
224
|
+
})
|
|
225
|
+
});
|
|
226
|
+
const data = await response.json();
|
|
227
|
+
if (data.error) {
|
|
228
|
+
throw new Error(data.error.message || "RPC error");
|
|
139
229
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
230
|
+
return data.result;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Handle incoming WebSocket messages
|
|
234
|
+
*/
|
|
235
|
+
handleMessage(data) {
|
|
236
|
+
if (data.type === "message") {
|
|
237
|
+
this.emit("message", {
|
|
238
|
+
from: data.from,
|
|
239
|
+
taskId: data.taskId,
|
|
240
|
+
payload: data.payload,
|
|
241
|
+
timestamp: data.timestamp || Date.now()
|
|
242
|
+
});
|
|
243
|
+
} else if (data.type === "task_update") {
|
|
244
|
+
this.emit("taskUpdate", data.task);
|
|
245
|
+
} else if (data.type === "pong") {
|
|
246
|
+
} else if (data.type === "welcome") {
|
|
247
|
+
this.agentId = data.agentId;
|
|
248
|
+
if (this.agentCard && this.ws?.readyState === 1) {
|
|
249
|
+
this.ws.send(JSON.stringify({ type: "update_card", agentCard: this.agentCard }));
|
|
250
|
+
}
|
|
251
|
+
} else if (data.type === "card_updated") {
|
|
252
|
+
} else if (data.type === "warning") {
|
|
253
|
+
console.warn("GopherHole warning:", data.message);
|
|
153
254
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Start ping interval
|
|
258
|
+
*/
|
|
259
|
+
startPing() {
|
|
260
|
+
this.pingInterval = setInterval(() => {
|
|
261
|
+
if (this.ws?.readyState === 1) {
|
|
262
|
+
this.ws.send(JSON.stringify({ type: "ping" }));
|
|
263
|
+
}
|
|
264
|
+
}, 3e4);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Stop ping interval
|
|
268
|
+
*/
|
|
269
|
+
stopPing() {
|
|
270
|
+
if (this.pingInterval) {
|
|
271
|
+
clearInterval(this.pingInterval);
|
|
272
|
+
this.pingInterval = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Schedule reconnection
|
|
277
|
+
*/
|
|
278
|
+
scheduleReconnect() {
|
|
279
|
+
if (this.reconnectTimer) return;
|
|
280
|
+
this.reconnectAttempts++;
|
|
281
|
+
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
282
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
283
|
+
this.reconnectTimer = null;
|
|
284
|
+
try {
|
|
285
|
+
await this.connect();
|
|
286
|
+
} catch {
|
|
287
|
+
}
|
|
288
|
+
}, delay);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get connection state
|
|
292
|
+
*/
|
|
293
|
+
get connected() {
|
|
294
|
+
return this.ws?.readyState === 1;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get the agent ID (available after connect)
|
|
298
|
+
*/
|
|
299
|
+
get id() {
|
|
300
|
+
return this.agentId;
|
|
301
|
+
}
|
|
302
|
+
// ============================================================
|
|
303
|
+
// DISCOVERY METHODS
|
|
304
|
+
// ============================================================
|
|
305
|
+
/**
|
|
306
|
+
* Discover public agents with comprehensive search
|
|
307
|
+
*/
|
|
308
|
+
async discover(options) {
|
|
309
|
+
const params = new URLSearchParams();
|
|
310
|
+
if (options?.query) params.set("q", options.query);
|
|
311
|
+
if (options?.category) params.set("category", options.category);
|
|
312
|
+
if (options?.tag) params.set("tag", options.tag);
|
|
313
|
+
if (options?.skillTag) params.set("skillTag", options.skillTag);
|
|
314
|
+
if (options?.contentMode) params.set("contentMode", options.contentMode);
|
|
315
|
+
if (options?.sort) params.set("sort", options.sort);
|
|
316
|
+
if (options?.limit) params.set("limit", String(options.limit));
|
|
317
|
+
if (options?.offset) params.set("offset", String(options.offset));
|
|
318
|
+
const response = await fetch(`${this.apiUrl}/api/discover/agents?${params}`, {
|
|
319
|
+
headers: {
|
|
320
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
return response.json();
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Search agents with fuzzy matching on description
|
|
327
|
+
*/
|
|
328
|
+
async searchAgents(query, options) {
|
|
329
|
+
return this.discover({ ...options, query });
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Find agents by category
|
|
333
|
+
*/
|
|
334
|
+
async findByCategory(category, options) {
|
|
335
|
+
return this.discover({ ...options, category });
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Find agents by tag
|
|
339
|
+
*/
|
|
340
|
+
async findByTag(tag, options) {
|
|
341
|
+
return this.discover({ ...options, tag });
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Find agents by skill tag (searches within agent skills)
|
|
345
|
+
*/
|
|
346
|
+
async findBySkillTag(skillTag, options) {
|
|
347
|
+
return this.discover({ ...options, skillTag });
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Find agents that support a specific input/output mode
|
|
351
|
+
*/
|
|
352
|
+
async findByContentMode(mode, options) {
|
|
353
|
+
return this.discover({ ...options, contentMode: mode });
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get top-rated agents
|
|
357
|
+
*/
|
|
358
|
+
async getTopRated(limit = 10) {
|
|
359
|
+
return this.discover({ sort: "rating", limit });
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get most popular agents (by usage)
|
|
363
|
+
*/
|
|
364
|
+
async getPopular(limit = 10) {
|
|
365
|
+
return this.discover({ sort: "popular", limit });
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Get featured/curated agents
|
|
369
|
+
*/
|
|
370
|
+
async getFeatured() {
|
|
371
|
+
const response = await fetch(`${this.apiUrl}/api/discover/featured`);
|
|
372
|
+
return response.json();
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get available categories
|
|
376
|
+
*/
|
|
377
|
+
async getCategories() {
|
|
378
|
+
const response = await fetch(`${this.apiUrl}/api/discover/categories`);
|
|
379
|
+
return response.json();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get detailed info about a public agent
|
|
383
|
+
*/
|
|
384
|
+
async getAgentInfo(agentId) {
|
|
385
|
+
const response = await fetch(`${this.apiUrl}/api/discover/agents/${agentId}`);
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
throw new Error("Agent not found");
|
|
388
|
+
}
|
|
389
|
+
return response.json();
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Rate an agent (requires authentication)
|
|
393
|
+
*/
|
|
394
|
+
async rateAgent(agentId, rating, review) {
|
|
395
|
+
const response = await fetch(`${this.apiUrl}/api/discover/agents/${agentId}/rate`, {
|
|
396
|
+
method: "POST",
|
|
397
|
+
headers: {
|
|
398
|
+
"Content-Type": "application/json",
|
|
399
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
400
|
+
},
|
|
401
|
+
body: JSON.stringify({ rating, review })
|
|
402
|
+
});
|
|
403
|
+
if (!response.ok) {
|
|
404
|
+
const error = await response.json();
|
|
405
|
+
throw new Error(error.error || "Failed to rate agent");
|
|
406
|
+
}
|
|
407
|
+
return response.json();
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get best agent for a task using smart matching
|
|
411
|
+
* Searches by query and returns the top-rated match
|
|
412
|
+
*/
|
|
413
|
+
async findBestAgent(query, options) {
|
|
414
|
+
const result = await this.discover({
|
|
415
|
+
query,
|
|
416
|
+
category: options?.category,
|
|
417
|
+
sort: "rating",
|
|
418
|
+
limit: 10
|
|
419
|
+
});
|
|
420
|
+
const agents = result.agents.filter((agent) => {
|
|
421
|
+
if (options?.minRating && agent.avgRating < options.minRating) return false;
|
|
422
|
+
if (options?.pricing === "free" && agent.pricing !== "free") return false;
|
|
423
|
+
if (options?.pricing === "paid" && agent.pricing === "free") return false;
|
|
424
|
+
return true;
|
|
425
|
+
});
|
|
426
|
+
return agents[0] || null;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Find agents similar to a given agent
|
|
430
|
+
*/
|
|
431
|
+
async findSimilar(agentId, limit = 5) {
|
|
432
|
+
const info = await this.getAgentInfo(agentId);
|
|
433
|
+
const agent = info.agent;
|
|
434
|
+
if (agent.category) {
|
|
435
|
+
const result = await this.discover({
|
|
436
|
+
category: agent.category,
|
|
437
|
+
sort: "rating",
|
|
438
|
+
limit: limit + 1
|
|
439
|
+
// +1 to exclude self
|
|
440
|
+
});
|
|
441
|
+
result.agents = result.agents.filter((a) => a.id !== agentId).slice(0, limit);
|
|
442
|
+
return result;
|
|
205
443
|
}
|
|
206
|
-
|
|
207
|
-
|
|
444
|
+
return this.getTopRated(limit);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
var index_default = GopherHole;
|
|
448
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
449
|
+
0 && (module.exports = {
|
|
450
|
+
GopherHole,
|
|
451
|
+
JsonRpcErrorCodes
|
|
452
|
+
});
|