@gopherhole/cli 0.1.0 ā 0.1.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 +806 -82
- package/package.json +3 -2
- package/src/index.ts +874 -80
package/dist/index.js
CHANGED
|
@@ -11,21 +11,316 @@ const inquirer_1 = __importDefault(require("inquirer"));
|
|
|
11
11
|
const ora_1 = __importDefault(require("ora"));
|
|
12
12
|
const config = new conf_1.default({ projectName: 'gopherhole' });
|
|
13
13
|
const API_URL = 'https://gopherhole.ai/api';
|
|
14
|
+
const WS_URL = 'wss://gopherhole.helixdata.workers.dev/ws';
|
|
15
|
+
// Brand colors
|
|
16
|
+
const brand = {
|
|
17
|
+
green: chalk_1.default.hex('#22c55e'), // gopher-500 - primary
|
|
18
|
+
greenBright: chalk_1.default.hex('#4ade80'), // gopher-400 - highlights
|
|
19
|
+
greenDark: chalk_1.default.hex('#16a34a'), // gopher-600 - emphasis
|
|
20
|
+
};
|
|
21
|
+
// Global verbose flag
|
|
22
|
+
let verbose = false;
|
|
23
|
+
function log(...args) {
|
|
24
|
+
if (verbose) {
|
|
25
|
+
console.log(chalk_1.default.gray('[verbose]'), ...args);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function logError(context, error) {
|
|
29
|
+
if (verbose) {
|
|
30
|
+
console.error(chalk_1.default.red(`[verbose:${context}]`), error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
14
33
|
const program = new commander_1.Command();
|
|
15
34
|
program
|
|
16
35
|
.name('gopherhole')
|
|
17
|
-
.description(
|
|
18
|
-
|
|
36
|
+
.description(`šæļø GopherHole CLI - Connect AI agents to the world
|
|
37
|
+
|
|
38
|
+
${chalk_1.default.bold('Quick Start:')}
|
|
39
|
+
$ gopherhole quickstart # Interactive setup wizard
|
|
40
|
+
$ gopherhole signup # Create an account
|
|
41
|
+
$ gopherhole agents create # Create your first agent
|
|
42
|
+
|
|
43
|
+
${chalk_1.default.bold('Examples:')}
|
|
44
|
+
$ gopherhole discover search "weather"
|
|
45
|
+
$ gopherhole send agent-abc123 "Hello!"
|
|
46
|
+
$ gopherhole agents list
|
|
47
|
+
|
|
48
|
+
${chalk_1.default.bold('Documentation:')}
|
|
49
|
+
https://gopherhole.ai/docs
|
|
50
|
+
`)
|
|
51
|
+
.version('0.1.0')
|
|
52
|
+
.option('-v, --verbose', 'Enable verbose output for debugging')
|
|
53
|
+
.hook('preAction', (thisCommand) => {
|
|
54
|
+
verbose = thisCommand.opts().verbose || false;
|
|
55
|
+
if (verbose) {
|
|
56
|
+
console.log(chalk_1.default.gray('[verbose] Verbose mode enabled'));
|
|
57
|
+
console.log(chalk_1.default.gray(`[verbose] API: ${API_URL}`));
|
|
58
|
+
console.log(chalk_1.default.gray(`[verbose] WebSocket: ${WS_URL}`));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// ========== QUICKSTART COMMAND ==========
|
|
62
|
+
program
|
|
63
|
+
.command('quickstart')
|
|
64
|
+
.description(`Interactive setup wizard for new users
|
|
65
|
+
|
|
66
|
+
${chalk_1.default.bold('What it does:')}
|
|
67
|
+
1. Creates an account (or logs you in)
|
|
68
|
+
2. Creates your first agent
|
|
69
|
+
3. Generates starter code
|
|
70
|
+
4. Shows next steps
|
|
71
|
+
|
|
72
|
+
${chalk_1.default.bold('Example:')}
|
|
73
|
+
$ gopherhole quickstart
|
|
74
|
+
`)
|
|
75
|
+
.action(async () => {
|
|
76
|
+
console.log(chalk_1.default.bold('\nšæļø Welcome to GopherHole!\n'));
|
|
77
|
+
console.log('This wizard will help you set up your first agent.\n');
|
|
78
|
+
let sessionId = config.get('sessionId');
|
|
79
|
+
// Step 1: Auth
|
|
80
|
+
if (sessionId) {
|
|
81
|
+
const user = config.get('user');
|
|
82
|
+
console.log(brand.green(`ā Already logged in as ${user?.email}\n`));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.log(chalk_1.default.bold('Step 1: Create an account\n'));
|
|
86
|
+
const { action } = await inquirer_1.default.prompt([
|
|
87
|
+
{
|
|
88
|
+
type: 'list',
|
|
89
|
+
name: 'action',
|
|
90
|
+
message: 'Do you have a GopherHole account?',
|
|
91
|
+
choices: [
|
|
92
|
+
{ name: 'No, create one for me', value: 'signup' },
|
|
93
|
+
{ name: 'Yes, log me in', value: 'login' },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
]);
|
|
97
|
+
if (action === 'signup') {
|
|
98
|
+
const { name, email, password } = await inquirer_1.default.prompt([
|
|
99
|
+
{ type: 'input', name: 'name', message: 'Your name:' },
|
|
100
|
+
{ type: 'input', name: 'email', message: 'Email:' },
|
|
101
|
+
{ type: 'password', name: 'password', message: 'Password (min 8 chars):' },
|
|
102
|
+
]);
|
|
103
|
+
const spinner = (0, ora_1.default)('Creating your account...').start();
|
|
104
|
+
log('POST /auth/signup', { email });
|
|
105
|
+
try {
|
|
106
|
+
const res = await fetch(`${API_URL}/auth/signup`, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers: { 'Content-Type': 'application/json' },
|
|
109
|
+
body: JSON.stringify({ name, email, password }),
|
|
110
|
+
});
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
const err = await res.json();
|
|
113
|
+
logError('signup', err);
|
|
114
|
+
throw new Error(err.error || 'Signup failed');
|
|
115
|
+
}
|
|
116
|
+
const data = await res.json();
|
|
117
|
+
config.set('sessionId', data.sessionId);
|
|
118
|
+
config.set('user', data.user);
|
|
119
|
+
config.set('tenant', data.tenant);
|
|
120
|
+
sessionId = data.sessionId;
|
|
121
|
+
spinner.succeed('Account created!');
|
|
122
|
+
log('Session ID stored');
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const { email, password } = await inquirer_1.default.prompt([
|
|
131
|
+
{ type: 'input', name: 'email', message: 'Email:' },
|
|
132
|
+
{ type: 'password', name: 'password', message: 'Password:' },
|
|
133
|
+
]);
|
|
134
|
+
const spinner = (0, ora_1.default)('Logging in...').start();
|
|
135
|
+
log('POST /auth/login', { email });
|
|
136
|
+
try {
|
|
137
|
+
const res = await fetch(`${API_URL}/auth/login`, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: { 'Content-Type': 'application/json' },
|
|
140
|
+
body: JSON.stringify({ email, password }),
|
|
141
|
+
});
|
|
142
|
+
if (!res.ok) {
|
|
143
|
+
const err = await res.json();
|
|
144
|
+
logError('login', err);
|
|
145
|
+
throw new Error(err.error || 'Login failed');
|
|
146
|
+
}
|
|
147
|
+
const data = await res.json();
|
|
148
|
+
config.set('sessionId', data.sessionId);
|
|
149
|
+
config.set('user', data.user);
|
|
150
|
+
config.set('tenant', data.tenant);
|
|
151
|
+
sessionId = data.sessionId;
|
|
152
|
+
spinner.succeed('Logged in!');
|
|
153
|
+
log('Session ID stored');
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Step 2: Create agent
|
|
162
|
+
console.log(chalk_1.default.bold('\nStep 2: Create your agent\n'));
|
|
163
|
+
const { agentName, agentDesc } = await inquirer_1.default.prompt([
|
|
164
|
+
{
|
|
165
|
+
type: 'input',
|
|
166
|
+
name: 'agentName',
|
|
167
|
+
message: 'Agent name:',
|
|
168
|
+
default: 'my-agent',
|
|
169
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'input',
|
|
173
|
+
name: 'agentDesc',
|
|
174
|
+
message: 'What does your agent do? (optional):',
|
|
175
|
+
},
|
|
176
|
+
]);
|
|
177
|
+
const spinner = (0, ora_1.default)('Creating agent...').start();
|
|
178
|
+
log('POST /agents', { name: agentName });
|
|
179
|
+
try {
|
|
180
|
+
const res = await fetch(`${API_URL}/agents`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: {
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
'X-Session-ID': sessionId,
|
|
185
|
+
},
|
|
186
|
+
body: JSON.stringify({ name: agentName, description: agentDesc }),
|
|
187
|
+
});
|
|
188
|
+
if (!res.ok) {
|
|
189
|
+
const err = await res.json();
|
|
190
|
+
logError('create agent', err);
|
|
191
|
+
throw new Error(err.error || 'Failed to create agent');
|
|
192
|
+
}
|
|
193
|
+
const data = await res.json();
|
|
194
|
+
spinner.succeed('Agent created!');
|
|
195
|
+
// Step 3: Show results
|
|
196
|
+
console.log(chalk_1.default.bold('\n⨠Your agent is ready!\n'));
|
|
197
|
+
console.log(chalk_1.default.bold(' Agent Details:'));
|
|
198
|
+
console.log(` Name: ${brand.green(data.agent.name)}`);
|
|
199
|
+
console.log(` ID: ${chalk_1.default.cyan(data.agent.id)}`);
|
|
200
|
+
console.log(` API Key: ${chalk_1.default.yellow(data.apiKey)}`);
|
|
201
|
+
console.log(chalk_1.default.bold('\n Connection Info:'));
|
|
202
|
+
console.log(` WebSocket: ${chalk_1.default.cyan(WS_URL)}`);
|
|
203
|
+
console.log(` REST API: ${chalk_1.default.cyan('https://gopherhole.ai/a2a')}`);
|
|
204
|
+
console.log(chalk_1.default.red('\n ā ļø Save your API key now - it won\'t be shown again!\n'));
|
|
205
|
+
// Step 4: Generate code
|
|
206
|
+
console.log(chalk_1.default.bold('Step 3: Quick start code\n'));
|
|
207
|
+
const { language } = await inquirer_1.default.prompt([
|
|
208
|
+
{
|
|
209
|
+
type: 'list',
|
|
210
|
+
name: 'language',
|
|
211
|
+
message: 'Which language?',
|
|
212
|
+
choices: [
|
|
213
|
+
{ name: 'TypeScript / JavaScript', value: 'ts' },
|
|
214
|
+
{ name: 'Python', value: 'py' },
|
|
215
|
+
{ name: 'Go', value: 'go' },
|
|
216
|
+
{ name: 'Just show me the curl command', value: 'curl' },
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
]);
|
|
220
|
+
console.log('');
|
|
221
|
+
if (language === 'ts') {
|
|
222
|
+
console.log(chalk_1.default.gray(' # Install the SDK'));
|
|
223
|
+
console.log(chalk_1.default.cyan(' npm install @gopherhole/sdk\n'));
|
|
224
|
+
console.log(chalk_1.default.gray(' # agent.ts'));
|
|
225
|
+
console.log(chalk_1.default.white(` import { GopherHole } from '@gopherhole/sdk';
|
|
226
|
+
|
|
227
|
+
const client = new GopherHole('${data.apiKey}');
|
|
228
|
+
|
|
229
|
+
async function main() {
|
|
230
|
+
await client.connect();
|
|
231
|
+
console.log('šæļø Connected to GopherHole!');
|
|
232
|
+
|
|
233
|
+
// Handle incoming messages
|
|
234
|
+
client.on('message', async (msg) => {
|
|
235
|
+
console.log('From:', msg.from, 'Message:', msg.text);
|
|
236
|
+
|
|
237
|
+
// Reply back
|
|
238
|
+
await client.send(msg.from, 'Hello! I got your message.');
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main();`));
|
|
243
|
+
}
|
|
244
|
+
else if (language === 'py') {
|
|
245
|
+
console.log(chalk_1.default.gray(' # Install the SDK'));
|
|
246
|
+
console.log(chalk_1.default.cyan(' pip install gopherhole\n'));
|
|
247
|
+
console.log(chalk_1.default.gray(' # agent.py'));
|
|
248
|
+
console.log(chalk_1.default.white(` from gopherhole import GopherHole
|
|
249
|
+
|
|
250
|
+
client = GopherHole("${data.apiKey}")
|
|
251
|
+
|
|
252
|
+
@client.on_message
|
|
253
|
+
async def handle_message(msg):
|
|
254
|
+
print(f"From: {msg.from_agent}, Message: {msg.text}")
|
|
255
|
+
await client.send(msg.from_agent, "Hello! I got your message.")
|
|
256
|
+
|
|
257
|
+
if __name__ == "__main__":
|
|
258
|
+
client.run()`));
|
|
259
|
+
}
|
|
260
|
+
else if (language === 'go') {
|
|
261
|
+
console.log(chalk_1.default.gray(' # Install the SDK'));
|
|
262
|
+
console.log(chalk_1.default.cyan(' go get github.com/gopherhole/gopherhole-go\n'));
|
|
263
|
+
console.log(chalk_1.default.gray(' // main.go'));
|
|
264
|
+
console.log(chalk_1.default.white(` package main
|
|
265
|
+
|
|
266
|
+
import (
|
|
267
|
+
"fmt"
|
|
268
|
+
gopherhole "github.com/gopherhole/gopherhole-go"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
func main() {
|
|
272
|
+
client := gopherhole.New("${data.apiKey}")
|
|
273
|
+
|
|
274
|
+
client.OnMessage(func(msg *gopherhole.Message) {
|
|
275
|
+
fmt.Printf("From: %s, Message: %s\\n", msg.From, msg.Text)
|
|
276
|
+
client.Send(msg.From, "Hello! I got your message.")
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
client.Connect()
|
|
280
|
+
}`));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
console.log(chalk_1.default.gray(' # Test with curl'));
|
|
284
|
+
console.log(chalk_1.default.white(` curl -X POST https://gopherhole.ai/a2a \\
|
|
285
|
+
-H "Authorization: Bearer ${data.apiKey}" \\
|
|
286
|
+
-H "Content-Type: application/json" \\
|
|
287
|
+
-d '{
|
|
288
|
+
"jsonrpc": "2.0",
|
|
289
|
+
"method": "message/send",
|
|
290
|
+
"params": {
|
|
291
|
+
"message": { "role": "user", "parts": [{ "kind": "text", "text": "Hello!" }] },
|
|
292
|
+
"configuration": { "agentId": "echo" }
|
|
293
|
+
},
|
|
294
|
+
"id": 1
|
|
295
|
+
}'`));
|
|
296
|
+
}
|
|
297
|
+
console.log(chalk_1.default.bold('\nš Next steps:\n'));
|
|
298
|
+
console.log(` ⢠Dashboard: ${chalk_1.default.cyan('https://gopherhole.ai/dashboard')}`);
|
|
299
|
+
console.log(` ⢠Docs: ${chalk_1.default.cyan('https://gopherhole.ai/docs')}`);
|
|
300
|
+
console.log(` ⢠Find agents: ${chalk_1.default.cyan('gopherhole discover search')}`);
|
|
301
|
+
console.log(` ⢠List yours: ${chalk_1.default.cyan('gopherhole agents list')}`);
|
|
302
|
+
console.log('');
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
19
309
|
// ========== AUTH COMMANDS ==========
|
|
20
310
|
program
|
|
21
311
|
.command('login')
|
|
22
|
-
.description(
|
|
312
|
+
.description(`Log in to GopherHole
|
|
313
|
+
|
|
314
|
+
${chalk_1.default.bold('Example:')}
|
|
315
|
+
$ gopherhole login
|
|
316
|
+
`)
|
|
23
317
|
.action(async () => {
|
|
24
318
|
const { email, password } = await inquirer_1.default.prompt([
|
|
25
319
|
{ type: 'input', name: 'email', message: 'Email:' },
|
|
26
320
|
{ type: 'password', name: 'password', message: 'Password:' },
|
|
27
321
|
]);
|
|
28
322
|
const spinner = (0, ora_1.default)('Logging in...').start();
|
|
323
|
+
log('POST /auth/login', { email });
|
|
29
324
|
try {
|
|
30
325
|
const res = await fetch(`${API_URL}/auth/login`, {
|
|
31
326
|
method: 'POST',
|
|
@@ -34,29 +329,40 @@ program
|
|
|
34
329
|
});
|
|
35
330
|
if (!res.ok) {
|
|
36
331
|
const err = await res.json();
|
|
332
|
+
logError('login', err);
|
|
37
333
|
throw new Error(err.error || 'Login failed');
|
|
38
334
|
}
|
|
39
335
|
const data = await res.json();
|
|
40
336
|
config.set('sessionId', data.sessionId);
|
|
41
337
|
config.set('user', data.user);
|
|
42
338
|
config.set('tenant', data.tenant);
|
|
43
|
-
spinner.succeed(`Logged in as ${
|
|
339
|
+
spinner.succeed(`Logged in as ${brand.green(data.user.email)}`);
|
|
340
|
+
log('Session stored in:', config.path);
|
|
44
341
|
}
|
|
45
342
|
catch (err) {
|
|
46
343
|
spinner.fail(chalk_1.default.red(err.message));
|
|
344
|
+
console.log(chalk_1.default.gray('\nTroubleshooting:'));
|
|
345
|
+
console.log(chalk_1.default.gray(' ⢠Check your email and password'));
|
|
346
|
+
console.log(chalk_1.default.gray(' ⢠Try: gopherhole signup (to create account)'));
|
|
347
|
+
console.log(chalk_1.default.gray(' ⢠Run with --verbose for more details'));
|
|
47
348
|
process.exit(1);
|
|
48
349
|
}
|
|
49
350
|
});
|
|
50
351
|
program
|
|
51
352
|
.command('signup')
|
|
52
|
-
.description(
|
|
353
|
+
.description(`Create a new GopherHole account
|
|
354
|
+
|
|
355
|
+
${chalk_1.default.bold('Example:')}
|
|
356
|
+
$ gopherhole signup
|
|
357
|
+
`)
|
|
53
358
|
.action(async () => {
|
|
54
359
|
const { name, email, password } = await inquirer_1.default.prompt([
|
|
55
360
|
{ type: 'input', name: 'name', message: 'Name:' },
|
|
56
361
|
{ type: 'input', name: 'email', message: 'Email:' },
|
|
57
|
-
{ type: 'password', name: 'password', message: 'Password:' },
|
|
362
|
+
{ type: 'password', name: 'password', message: 'Password (min 8 chars):' },
|
|
58
363
|
]);
|
|
59
364
|
const spinner = (0, ora_1.default)('Creating account...').start();
|
|
365
|
+
log('POST /auth/signup', { email });
|
|
60
366
|
try {
|
|
61
367
|
const res = await fetch(`${API_URL}/auth/signup`, {
|
|
62
368
|
method: 'POST',
|
|
@@ -65,13 +371,15 @@ program
|
|
|
65
371
|
});
|
|
66
372
|
if (!res.ok) {
|
|
67
373
|
const err = await res.json();
|
|
374
|
+
logError('signup', err);
|
|
68
375
|
throw new Error(err.error || 'Signup failed');
|
|
69
376
|
}
|
|
70
377
|
const data = await res.json();
|
|
71
378
|
config.set('sessionId', data.sessionId);
|
|
72
379
|
config.set('user', data.user);
|
|
73
380
|
config.set('tenant', data.tenant);
|
|
74
|
-
spinner.succeed(`Account created! Logged in as ${
|
|
381
|
+
spinner.succeed(`Account created! Logged in as ${brand.green(data.user.email)}`);
|
|
382
|
+
console.log(chalk_1.default.gray('\nNext: gopherhole agents create'));
|
|
75
383
|
}
|
|
76
384
|
catch (err) {
|
|
77
385
|
spinner.fail(chalk_1.default.red(err.message));
|
|
@@ -83,52 +391,79 @@ program
|
|
|
83
391
|
.description('Log out of GopherHole')
|
|
84
392
|
.action(() => {
|
|
85
393
|
config.clear();
|
|
86
|
-
console.log(
|
|
394
|
+
console.log(brand.green('ā Logged out successfully'));
|
|
395
|
+
log('Config cleared:', config.path);
|
|
87
396
|
});
|
|
88
397
|
program
|
|
89
398
|
.command('whoami')
|
|
90
399
|
.description('Show current logged in user')
|
|
91
400
|
.action(() => {
|
|
92
401
|
const user = config.get('user');
|
|
402
|
+
const tenant = config.get('tenant');
|
|
93
403
|
if (!user) {
|
|
94
|
-
console.log(chalk_1.default.yellow('Not logged in.
|
|
404
|
+
console.log(chalk_1.default.yellow('Not logged in.'));
|
|
405
|
+
console.log(chalk_1.default.gray('\nTo log in: gopherhole login'));
|
|
406
|
+
console.log(chalk_1.default.gray('To sign up: gopherhole signup'));
|
|
95
407
|
process.exit(1);
|
|
96
408
|
}
|
|
97
|
-
console.log(
|
|
409
|
+
console.log(`\n ${chalk_1.default.bold('User:')} ${user.name} <${user.email}>`);
|
|
410
|
+
console.log(` ${chalk_1.default.bold('Org:')} ${tenant?.name || 'Personal'}`);
|
|
411
|
+
console.log(` ${chalk_1.default.bold('Config:')} ${config.path}\n`);
|
|
98
412
|
});
|
|
99
413
|
// ========== AGENT COMMANDS ==========
|
|
100
|
-
const agents = program
|
|
414
|
+
const agents = program
|
|
415
|
+
.command('agents')
|
|
416
|
+
.description(`Manage your agents
|
|
417
|
+
|
|
418
|
+
${chalk_1.default.bold('Examples:')}
|
|
419
|
+
$ gopherhole agents list
|
|
420
|
+
$ gopherhole agents create --name "my-bot"
|
|
421
|
+
$ gopherhole agents delete agent-abc123
|
|
422
|
+
`);
|
|
101
423
|
agents
|
|
102
424
|
.command('list')
|
|
103
425
|
.description('List your agents')
|
|
104
|
-
.
|
|
426
|
+
.option('--json', 'Output as JSON')
|
|
427
|
+
.action(async (options) => {
|
|
105
428
|
const sessionId = config.get('sessionId');
|
|
106
429
|
if (!sessionId) {
|
|
107
|
-
console.log(chalk_1.default.yellow('Not logged in.
|
|
430
|
+
console.log(chalk_1.default.yellow('Not logged in.'));
|
|
431
|
+
console.log(chalk_1.default.gray('Run: gopherhole login'));
|
|
108
432
|
process.exit(1);
|
|
109
433
|
}
|
|
110
434
|
const spinner = (0, ora_1.default)('Fetching agents...').start();
|
|
435
|
+
log('GET /agents');
|
|
111
436
|
try {
|
|
112
437
|
const res = await fetch(`${API_URL}/agents`, {
|
|
113
438
|
headers: { 'X-Session-ID': sessionId },
|
|
114
439
|
});
|
|
115
|
-
if (!res.ok)
|
|
440
|
+
if (!res.ok) {
|
|
441
|
+
const err = await res.json();
|
|
442
|
+
logError('list', err);
|
|
116
443
|
throw new Error('Failed to fetch agents');
|
|
444
|
+
}
|
|
117
445
|
const data = await res.json();
|
|
118
446
|
spinner.stop();
|
|
447
|
+
if (options.json) {
|
|
448
|
+
console.log(JSON.stringify(data.agents, null, 2));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
119
451
|
if (data.agents.length === 0) {
|
|
120
|
-
console.log(chalk_1.default.yellow('
|
|
452
|
+
console.log(chalk_1.default.yellow('\nNo agents yet.\n'));
|
|
453
|
+
console.log(chalk_1.default.gray('Create one: gopherhole agents create'));
|
|
454
|
+
console.log(chalk_1.default.gray('Or try: gopherhole quickstart\n'));
|
|
121
455
|
return;
|
|
122
456
|
}
|
|
123
|
-
console.log(chalk_1.default.bold('\
|
|
457
|
+
console.log(chalk_1.default.bold('\nšæļø Your Agents:\n'));
|
|
124
458
|
for (const agent of data.agents) {
|
|
125
|
-
const status = agent.status === 'active' ?
|
|
126
|
-
console.log(` ${status} ${chalk_1.default.bold(agent.name)}
|
|
459
|
+
const status = agent.status === 'active' ? brand.green('ā') : chalk_1.default.red('ā');
|
|
460
|
+
console.log(` ${status} ${chalk_1.default.bold(agent.name)}`);
|
|
461
|
+
console.log(` ID: ${chalk_1.default.cyan(agent.id)}`);
|
|
127
462
|
if (agent.description) {
|
|
128
463
|
console.log(` ${chalk_1.default.gray(agent.description)}`);
|
|
129
464
|
}
|
|
465
|
+
console.log('');
|
|
130
466
|
}
|
|
131
|
-
console.log('');
|
|
132
467
|
}
|
|
133
468
|
catch (err) {
|
|
134
469
|
spinner.fail(chalk_1.default.red(err.message));
|
|
@@ -137,25 +472,37 @@ agents
|
|
|
137
472
|
});
|
|
138
473
|
agents
|
|
139
474
|
.command('create')
|
|
140
|
-
.description(
|
|
475
|
+
.description(`Create a new agent
|
|
476
|
+
|
|
477
|
+
${chalk_1.default.bold('Examples:')}
|
|
478
|
+
$ gopherhole agents create
|
|
479
|
+
$ gopherhole agents create --name "weather-bot" --description "Gets weather"
|
|
480
|
+
`)
|
|
141
481
|
.option('-n, --name <name>', 'Agent name')
|
|
142
482
|
.option('-d, --description <description>', 'Agent description')
|
|
143
483
|
.action(async (options) => {
|
|
144
484
|
const sessionId = config.get('sessionId');
|
|
145
485
|
if (!sessionId) {
|
|
146
|
-
console.log(chalk_1.default.yellow('Not logged in.
|
|
486
|
+
console.log(chalk_1.default.yellow('Not logged in.'));
|
|
487
|
+
console.log(chalk_1.default.gray('Run: gopherhole login'));
|
|
147
488
|
process.exit(1);
|
|
148
489
|
}
|
|
149
490
|
let { name, description } = options;
|
|
150
491
|
if (!name) {
|
|
151
492
|
const answers = await inquirer_1.default.prompt([
|
|
152
|
-
{
|
|
493
|
+
{
|
|
494
|
+
type: 'input',
|
|
495
|
+
name: 'name',
|
|
496
|
+
message: 'Agent name:',
|
|
497
|
+
validate: (input) => input.length > 0 || 'Name is required',
|
|
498
|
+
},
|
|
153
499
|
{ type: 'input', name: 'description', message: 'Description (optional):' },
|
|
154
500
|
]);
|
|
155
501
|
name = answers.name;
|
|
156
502
|
description = answers.description;
|
|
157
503
|
}
|
|
158
504
|
const spinner = (0, ora_1.default)('Creating agent...').start();
|
|
505
|
+
log('POST /agents', { name, description });
|
|
159
506
|
try {
|
|
160
507
|
const res = await fetch(`${API_URL}/agents`, {
|
|
161
508
|
method: 'POST',
|
|
@@ -167,27 +514,23 @@ agents
|
|
|
167
514
|
});
|
|
168
515
|
if (!res.ok) {
|
|
169
516
|
const err = await res.json();
|
|
517
|
+
logError('create', err);
|
|
170
518
|
throw new Error(err.error || 'Failed to create agent');
|
|
171
519
|
}
|
|
172
520
|
const data = await res.json();
|
|
173
|
-
spinner.succeed(`Agent created: ${
|
|
174
|
-
console.log('');
|
|
175
|
-
console.log(`
|
|
176
|
-
console.log(`
|
|
521
|
+
spinner.succeed(`Agent created: ${brand.green(data.agent.name)}`);
|
|
522
|
+
console.log(chalk_1.default.bold('\n Agent Details:'));
|
|
523
|
+
console.log(` ID: ${chalk_1.default.cyan(data.agent.id)}`);
|
|
524
|
+
console.log(` API Key: ${chalk_1.default.yellow(data.apiKey)}`);
|
|
525
|
+
console.log(chalk_1.default.bold('\n Connection:'));
|
|
526
|
+
console.log(` WebSocket: ${chalk_1.default.cyan(WS_URL)}`);
|
|
527
|
+
console.log(` REST API: ${chalk_1.default.cyan('https://gopherhole.ai/a2a')}`);
|
|
528
|
+
console.log(chalk_1.default.red('\n ā ļø Save your API key now - it won\'t be shown again!'));
|
|
529
|
+
console.log(chalk_1.default.bold('\n Quick test:'));
|
|
530
|
+
console.log(chalk_1.default.white(` curl -H "Authorization: Bearer ${data.apiKey}" \\
|
|
531
|
+
https://gopherhole.ai/a2a -d '{"jsonrpc":"2.0","method":"agent/info","id":1}'`));
|
|
532
|
+
console.log(chalk_1.default.gray('\n Full docs: https://gopherhole.ai/docs'));
|
|
177
533
|
console.log('');
|
|
178
|
-
console.log(chalk_1.default.gray('Save this API key - it won\'t be shown again!'));
|
|
179
|
-
console.log('');
|
|
180
|
-
console.log(chalk_1.default.bold('Quick start:'));
|
|
181
|
-
console.log(chalk_1.default.gray(`
|
|
182
|
-
import { GopherHole } from '@gopherhole/sdk';
|
|
183
|
-
|
|
184
|
-
const hub = new GopherHole('${data.apiKey}');
|
|
185
|
-
await hub.connect();
|
|
186
|
-
|
|
187
|
-
hub.on('message', (msg) => {
|
|
188
|
-
console.log('Received:', msg);
|
|
189
|
-
});
|
|
190
|
-
`));
|
|
191
534
|
}
|
|
192
535
|
catch (err) {
|
|
193
536
|
spinner.fail(chalk_1.default.red(err.message));
|
|
@@ -196,33 +539,45 @@ agents
|
|
|
196
539
|
});
|
|
197
540
|
agents
|
|
198
541
|
.command('delete <agentId>')
|
|
199
|
-
.description(
|
|
200
|
-
|
|
542
|
+
.description(`Delete an agent
|
|
543
|
+
|
|
544
|
+
${chalk_1.default.bold('Example:')}
|
|
545
|
+
$ gopherhole agents delete agent-abc123
|
|
546
|
+
`)
|
|
547
|
+
.option('-f, --force', 'Skip confirmation')
|
|
548
|
+
.action(async (agentId, options) => {
|
|
201
549
|
const sessionId = config.get('sessionId');
|
|
202
550
|
if (!sessionId) {
|
|
203
|
-
console.log(chalk_1.default.yellow('Not logged in.
|
|
551
|
+
console.log(chalk_1.default.yellow('Not logged in.'));
|
|
552
|
+
console.log(chalk_1.default.gray('Run: gopherhole login'));
|
|
204
553
|
process.exit(1);
|
|
205
554
|
}
|
|
206
|
-
|
|
207
|
-
{
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
555
|
+
if (!options.force) {
|
|
556
|
+
const { confirm } = await inquirer_1.default.prompt([
|
|
557
|
+
{
|
|
558
|
+
type: 'confirm',
|
|
559
|
+
name: 'confirm',
|
|
560
|
+
message: `Delete agent ${chalk_1.default.cyan(agentId)}? This cannot be undone.`,
|
|
561
|
+
default: false,
|
|
562
|
+
},
|
|
563
|
+
]);
|
|
564
|
+
if (!confirm) {
|
|
565
|
+
console.log('Cancelled.');
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
217
568
|
}
|
|
218
569
|
const spinner = (0, ora_1.default)('Deleting agent...').start();
|
|
570
|
+
log('DELETE /agents/' + agentId);
|
|
219
571
|
try {
|
|
220
572
|
const res = await fetch(`${API_URL}/agents/${agentId}`, {
|
|
221
573
|
method: 'DELETE',
|
|
222
574
|
headers: { 'X-Session-ID': sessionId },
|
|
223
575
|
});
|
|
224
|
-
if (!res.ok)
|
|
225
|
-
|
|
576
|
+
if (!res.ok) {
|
|
577
|
+
const err = await res.json();
|
|
578
|
+
logError('delete', err);
|
|
579
|
+
throw new Error(err.error || 'Failed to delete agent');
|
|
580
|
+
}
|
|
226
581
|
spinner.succeed('Agent deleted');
|
|
227
582
|
}
|
|
228
583
|
catch (err) {
|
|
@@ -233,21 +588,31 @@ agents
|
|
|
233
588
|
// ========== INIT COMMAND ==========
|
|
234
589
|
program
|
|
235
590
|
.command('init')
|
|
236
|
-
.description(
|
|
591
|
+
.description(`Initialize GopherHole in current directory
|
|
592
|
+
|
|
593
|
+
${chalk_1.default.bold('What it does:')}
|
|
594
|
+
⢠Creates .env with your API key
|
|
595
|
+
⢠Generates starter code (agent.ts)
|
|
596
|
+
⢠Sets up package.json if needed
|
|
597
|
+
|
|
598
|
+
${chalk_1.default.bold('Example:')}
|
|
599
|
+
$ mkdir my-agent && cd my-agent
|
|
600
|
+
$ gopherhole init
|
|
601
|
+
`)
|
|
237
602
|
.action(async () => {
|
|
238
|
-
console.log(chalk_1.default.bold('\nšæļø GopherHole
|
|
603
|
+
console.log(chalk_1.default.bold('\nšæļø Initialize GopherHole Project\n'));
|
|
239
604
|
let sessionId = config.get('sessionId');
|
|
240
605
|
// Check if logged in
|
|
241
606
|
if (!sessionId) {
|
|
242
|
-
console.log('
|
|
607
|
+
console.log(chalk_1.default.yellow('Not logged in yet.\n'));
|
|
243
608
|
const { action } = await inquirer_1.default.prompt([
|
|
244
609
|
{
|
|
245
610
|
type: 'list',
|
|
246
611
|
name: 'action',
|
|
247
|
-
message: '
|
|
612
|
+
message: 'Choose:',
|
|
248
613
|
choices: [
|
|
249
|
-
{ name: '
|
|
250
|
-
{ name: '
|
|
614
|
+
{ name: 'Create account', value: 'signup' },
|
|
615
|
+
{ name: 'Log in', value: 'login' },
|
|
251
616
|
],
|
|
252
617
|
},
|
|
253
618
|
]);
|
|
@@ -320,52 +685,90 @@ program
|
|
|
320
685
|
spinner.succeed('Agent created!');
|
|
321
686
|
// Write .env file
|
|
322
687
|
const fs = await import('fs');
|
|
323
|
-
fs.writeFileSync('.env',
|
|
324
|
-
|
|
688
|
+
fs.writeFileSync('.env', `# GopherHole Configuration
|
|
689
|
+
GOPHERHOLE_API_KEY=${data.apiKey}
|
|
690
|
+
GOPHERHOLE_AGENT_ID=${data.agent.id}
|
|
691
|
+
`);
|
|
692
|
+
console.log(brand.green('ā Created .env'));
|
|
325
693
|
// Write example code
|
|
326
694
|
const exampleCode = `import { GopherHole } from '@gopherhole/sdk';
|
|
695
|
+
import 'dotenv/config';
|
|
327
696
|
|
|
328
|
-
const
|
|
697
|
+
const client = new GopherHole(process.env.GOPHERHOLE_API_KEY!);
|
|
329
698
|
|
|
330
699
|
async function main() {
|
|
331
|
-
await
|
|
332
|
-
console.log('šæļø Connected to GopherHole!');
|
|
700
|
+
await client.connect();
|
|
701
|
+
console.log('šæļø Connected to GopherHole as ${agentName}!');
|
|
333
702
|
|
|
334
|
-
|
|
335
|
-
|
|
703
|
+
// Handle incoming messages
|
|
704
|
+
client.on('message', async (msg) => {
|
|
705
|
+
console.log(\`š© Message from \${msg.from}:\`, msg.text);
|
|
336
706
|
|
|
337
|
-
//
|
|
338
|
-
await
|
|
707
|
+
// Echo back
|
|
708
|
+
await client.send(msg.from, \`You said: \${msg.text}\`);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// Keep alive
|
|
712
|
+
process.on('SIGINT', async () => {
|
|
713
|
+
console.log('\\nš Disconnecting...');
|
|
714
|
+
await client.disconnect();
|
|
715
|
+
process.exit(0);
|
|
339
716
|
});
|
|
340
717
|
}
|
|
341
718
|
|
|
342
719
|
main().catch(console.error);
|
|
343
720
|
`;
|
|
344
721
|
fs.writeFileSync('agent.ts', exampleCode);
|
|
345
|
-
console.log(
|
|
346
|
-
|
|
722
|
+
console.log(brand.green('ā Created agent.ts'));
|
|
723
|
+
// Create package.json if it doesn't exist
|
|
724
|
+
if (!fs.existsSync('package.json')) {
|
|
725
|
+
const pkg = {
|
|
726
|
+
name: agentName,
|
|
727
|
+
version: '0.1.0',
|
|
728
|
+
type: 'module',
|
|
729
|
+
scripts: {
|
|
730
|
+
start: 'tsx agent.ts',
|
|
731
|
+
dev: 'tsx watch agent.ts',
|
|
732
|
+
},
|
|
733
|
+
dependencies: {
|
|
734
|
+
'@gopherhole/sdk': '^0.1.0',
|
|
735
|
+
dotenv: '^16.0.0',
|
|
736
|
+
},
|
|
737
|
+
devDependencies: {
|
|
738
|
+
tsx: '^4.0.0',
|
|
739
|
+
typescript: '^5.0.0',
|
|
740
|
+
},
|
|
741
|
+
};
|
|
742
|
+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
|
|
743
|
+
console.log(brand.green('ā Created package.json'));
|
|
744
|
+
}
|
|
745
|
+
console.log(chalk_1.default.bold('\nš Project initialized!\n'));
|
|
347
746
|
console.log(chalk_1.default.bold('Next steps:'));
|
|
348
|
-
console.log('');
|
|
349
|
-
console.log('
|
|
350
|
-
console.log(chalk_1.default.cyan(' npm install @gopherhole/sdk'));
|
|
351
|
-
console.log('');
|
|
352
|
-
console.log(' 2. Run your agent:');
|
|
353
|
-
console.log(chalk_1.default.cyan(' npx ts-node agent.ts'));
|
|
747
|
+
console.log(chalk_1.default.cyan(' npm install'));
|
|
748
|
+
console.log(chalk_1.default.cyan(' npm start'));
|
|
354
749
|
console.log('');
|
|
355
750
|
console.log(chalk_1.default.gray(`Dashboard: https://gopherhole.ai/dashboard`));
|
|
751
|
+
console.log(chalk_1.default.gray(`Docs: https://gopherhole.ai/docs`));
|
|
356
752
|
console.log('');
|
|
357
753
|
});
|
|
358
754
|
// ========== SEND COMMAND ==========
|
|
359
755
|
program
|
|
360
756
|
.command('send <agentId> <message>')
|
|
361
|
-
.description(
|
|
757
|
+
.description(`Send a message to an agent (for testing)
|
|
758
|
+
|
|
759
|
+
${chalk_1.default.bold('Examples:')}
|
|
760
|
+
$ gopherhole send echo "Hello!"
|
|
761
|
+
$ gopherhole send agent-abc123 "What's the weather?"
|
|
762
|
+
`)
|
|
362
763
|
.action(async (agentId, message) => {
|
|
363
764
|
const sessionId = config.get('sessionId');
|
|
364
765
|
if (!sessionId) {
|
|
365
|
-
console.log(chalk_1.default.yellow('Not logged in.
|
|
766
|
+
console.log(chalk_1.default.yellow('Not logged in.'));
|
|
767
|
+
console.log(chalk_1.default.gray('Run: gopherhole login'));
|
|
366
768
|
process.exit(1);
|
|
367
769
|
}
|
|
368
|
-
const spinner = (0, ora_1.default)(
|
|
770
|
+
const spinner = (0, ora_1.default)(`Sending to ${agentId}...`).start();
|
|
771
|
+
log('POST /a2a message/send', { to: agentId, message });
|
|
369
772
|
try {
|
|
370
773
|
const res = await fetch(`${API_URL}/../a2a`, {
|
|
371
774
|
method: 'POST',
|
|
@@ -384,13 +787,334 @@ program
|
|
|
384
787
|
}),
|
|
385
788
|
});
|
|
386
789
|
const data = await res.json();
|
|
790
|
+
if (data.error) {
|
|
791
|
+
logError('send', data.error);
|
|
792
|
+
throw new Error(data.error.message || 'Send failed');
|
|
793
|
+
}
|
|
387
794
|
spinner.succeed('Message sent!');
|
|
388
|
-
|
|
795
|
+
if (data.result?.task) {
|
|
796
|
+
console.log(chalk_1.default.gray(`\nTask ID: ${data.result.task.id}`));
|
|
797
|
+
console.log(chalk_1.default.gray(`Status: ${data.result.task.status.state}`));
|
|
798
|
+
}
|
|
799
|
+
if (verbose) {
|
|
800
|
+
console.log(chalk_1.default.gray('\nFull response:'));
|
|
801
|
+
console.log(JSON.stringify(data, null, 2));
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
catch (err) {
|
|
805
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
// ========== DISCOVER COMMANDS ==========
|
|
810
|
+
const discover = program
|
|
811
|
+
.command('discover')
|
|
812
|
+
.description(`Discover public agents
|
|
813
|
+
|
|
814
|
+
${chalk_1.default.bold('Examples:')}
|
|
815
|
+
$ gopherhole discover search "weather"
|
|
816
|
+
$ gopherhole discover top
|
|
817
|
+
$ gopherhole discover info agent-abc123
|
|
818
|
+
`);
|
|
819
|
+
discover
|
|
820
|
+
.command('search [query]')
|
|
821
|
+
.description(`Search for agents
|
|
822
|
+
|
|
823
|
+
${chalk_1.default.bold('Examples:')}
|
|
824
|
+
$ gopherhole discover search
|
|
825
|
+
$ gopherhole discover search "email assistant"
|
|
826
|
+
$ gopherhole discover search --category productivity
|
|
827
|
+
$ gopherhole discover search --tag ai --sort popular
|
|
828
|
+
`)
|
|
829
|
+
.option('-c, --category <category>', 'Filter by category')
|
|
830
|
+
.option('-t, --tag <tag>', 'Filter by tag')
|
|
831
|
+
.option('-s, --sort <sort>', 'Sort by: rating, popular, recent', 'rating')
|
|
832
|
+
.option('-l, --limit <limit>', 'Number of results', '20')
|
|
833
|
+
.action(async (query, options) => {
|
|
834
|
+
const spinner = (0, ora_1.default)('Searching agents...').start();
|
|
835
|
+
try {
|
|
836
|
+
const params = new URLSearchParams();
|
|
837
|
+
if (query)
|
|
838
|
+
params.set('q', query);
|
|
839
|
+
if (options.category)
|
|
840
|
+
params.set('category', options.category);
|
|
841
|
+
if (options.tag)
|
|
842
|
+
params.set('tag', options.tag);
|
|
843
|
+
if (options.sort)
|
|
844
|
+
params.set('sort', options.sort);
|
|
845
|
+
if (options.limit)
|
|
846
|
+
params.set('limit', options.limit);
|
|
847
|
+
log('GET /discover/agents?' + params.toString());
|
|
848
|
+
const res = await fetch(`${API_URL}/discover/agents?${params}`);
|
|
849
|
+
const data = await res.json();
|
|
850
|
+
spinner.stop();
|
|
851
|
+
if (data.agents.length === 0) {
|
|
852
|
+
console.log(chalk_1.default.yellow('\nNo agents found.'));
|
|
853
|
+
console.log(chalk_1.default.gray('Try: gopherhole discover search (without query)'));
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
console.log(chalk_1.default.bold(`\nš Found ${data.agents.length} agents:\n`));
|
|
857
|
+
for (const agent of data.agents) {
|
|
858
|
+
const stars = 'ā
'.repeat(Math.round(agent.avgRating)) + 'ā'.repeat(5 - Math.round(agent.avgRating));
|
|
859
|
+
const pricing = agent.pricing === 'free' ? brand.green('FREE') :
|
|
860
|
+
agent.pricing === 'paid' ? chalk_1.default.blue('PAID') : chalk_1.default.gray('CONTACT');
|
|
861
|
+
console.log(` ${chalk_1.default.bold(agent.name)} ${chalk_1.default.yellow(stars)} (${agent.ratingCount})`);
|
|
862
|
+
console.log(` ${chalk_1.default.gray(agent.description || 'No description')}`);
|
|
863
|
+
console.log(` ${chalk_1.default.cyan(agent.id)} | ${pricing} | ${agent.category || 'uncategorized'}`);
|
|
864
|
+
console.log('');
|
|
865
|
+
}
|
|
866
|
+
console.log(chalk_1.default.gray(`Tip: gopherhole discover info <agent-id> for details\n`));
|
|
867
|
+
}
|
|
868
|
+
catch (err) {
|
|
869
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
870
|
+
process.exit(1);
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
discover
|
|
874
|
+
.command('categories')
|
|
875
|
+
.description('List available categories')
|
|
876
|
+
.action(async () => {
|
|
877
|
+
const spinner = (0, ora_1.default)('Fetching categories...').start();
|
|
878
|
+
try {
|
|
879
|
+
const res = await fetch(`${API_URL}/discover/categories`);
|
|
880
|
+
const data = await res.json();
|
|
881
|
+
spinner.stop();
|
|
882
|
+
if (data.categories.length === 0) {
|
|
883
|
+
console.log(chalk_1.default.yellow('No categories yet'));
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
console.log(chalk_1.default.bold('\nš Categories:\n'));
|
|
887
|
+
for (const cat of data.categories) {
|
|
888
|
+
console.log(` ${chalk_1.default.bold(cat.name)} (${cat.count} agents)`);
|
|
889
|
+
}
|
|
890
|
+
console.log(chalk_1.default.gray('\nSearch by category: gopherhole discover search --category <name>\n'));
|
|
891
|
+
}
|
|
892
|
+
catch (err) {
|
|
893
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
894
|
+
process.exit(1);
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
discover
|
|
898
|
+
.command('featured')
|
|
899
|
+
.description('Show featured agents')
|
|
900
|
+
.action(async () => {
|
|
901
|
+
const spinner = (0, ora_1.default)('Fetching featured agents...').start();
|
|
902
|
+
try {
|
|
903
|
+
const res = await fetch(`${API_URL}/discover/featured`);
|
|
904
|
+
const data = await res.json();
|
|
905
|
+
spinner.stop();
|
|
906
|
+
if (data.featured.length === 0) {
|
|
907
|
+
console.log(chalk_1.default.yellow('No featured agents yet'));
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
console.log(chalk_1.default.bold('\nā Featured Agents:\n'));
|
|
911
|
+
for (const agent of data.featured) {
|
|
912
|
+
const stars = 'ā
'.repeat(Math.round(agent.avgRating)) + 'ā'.repeat(5 - Math.round(agent.avgRating));
|
|
913
|
+
console.log(` ${chalk_1.default.bold(agent.name)} ${chalk_1.default.yellow(stars)}`);
|
|
914
|
+
console.log(` ${chalk_1.default.gray(agent.description || 'No description')}`);
|
|
915
|
+
console.log(` ${chalk_1.default.cyan(agent.id)}`);
|
|
916
|
+
console.log('');
|
|
917
|
+
}
|
|
389
918
|
}
|
|
390
919
|
catch (err) {
|
|
391
920
|
spinner.fail(chalk_1.default.red(err.message));
|
|
392
921
|
process.exit(1);
|
|
393
922
|
}
|
|
394
923
|
});
|
|
924
|
+
discover
|
|
925
|
+
.command('info <agentId>')
|
|
926
|
+
.description(`Get detailed info about an agent
|
|
927
|
+
|
|
928
|
+
${chalk_1.default.bold('Example:')}
|
|
929
|
+
$ gopherhole discover info agent-abc123
|
|
930
|
+
`)
|
|
931
|
+
.action(async (agentId) => {
|
|
932
|
+
const spinner = (0, ora_1.default)('Fetching agent info...').start();
|
|
933
|
+
try {
|
|
934
|
+
log('GET /discover/agents/' + agentId);
|
|
935
|
+
const res = await fetch(`${API_URL}/discover/agents/${agentId}`);
|
|
936
|
+
if (!res.ok) {
|
|
937
|
+
throw new Error('Agent not found');
|
|
938
|
+
}
|
|
939
|
+
const data = await res.json();
|
|
940
|
+
spinner.stop();
|
|
941
|
+
const agent = data.agent;
|
|
942
|
+
const stars = 'ā
'.repeat(Math.round(agent.stats.avgRating)) + 'ā'.repeat(5 - Math.round(agent.stats.avgRating));
|
|
943
|
+
console.log(chalk_1.default.bold(`\nšæļø ${agent.name}\n`));
|
|
944
|
+
console.log(` ${chalk_1.default.gray(agent.description || 'No description')}`);
|
|
945
|
+
console.log('');
|
|
946
|
+
console.log(` ${chalk_1.default.bold('Rating:')} ${chalk_1.default.yellow(stars)} ${agent.stats.avgRating.toFixed(1)}/5 (${agent.stats.ratingCount} reviews)`);
|
|
947
|
+
console.log(` ${chalk_1.default.bold('Messages:')} ${agent.stats.totalMessages.toLocaleString()}`);
|
|
948
|
+
console.log(` ${chalk_1.default.bold('Success:')} ${(agent.stats.successRate * 100).toFixed(1)}%`);
|
|
949
|
+
console.log(` ${chalk_1.default.bold('Response:')} ${agent.stats.avgResponseTime}ms avg`);
|
|
950
|
+
console.log(` ${chalk_1.default.bold('Pricing:')} ${agent.pricing}`);
|
|
951
|
+
console.log(` ${chalk_1.default.bold('Category:')} ${agent.category || 'None'}`);
|
|
952
|
+
console.log(` ${chalk_1.default.bold('By:')} ${agent.tenantName}`);
|
|
953
|
+
if (agent.tags.length > 0) {
|
|
954
|
+
console.log(` ${chalk_1.default.bold('Tags:')} ${agent.tags.join(', ')}`);
|
|
955
|
+
}
|
|
956
|
+
if (agent.websiteUrl) {
|
|
957
|
+
console.log(` ${chalk_1.default.bold('Website:')} ${chalk_1.default.cyan(agent.websiteUrl)}`);
|
|
958
|
+
}
|
|
959
|
+
if (agent.docsUrl) {
|
|
960
|
+
console.log(` ${chalk_1.default.bold('Docs:')} ${chalk_1.default.cyan(agent.docsUrl)}`);
|
|
961
|
+
}
|
|
962
|
+
if (agent.agentCard?.skills?.length) {
|
|
963
|
+
console.log(chalk_1.default.bold('\n Skills:'));
|
|
964
|
+
for (const skill of agent.agentCard.skills) {
|
|
965
|
+
console.log(` ⢠${skill.name}${skill.description ? ` - ${chalk_1.default.gray(skill.description)}` : ''}`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (data.reviews.length > 0) {
|
|
969
|
+
console.log(chalk_1.default.bold('\n Recent Reviews:'));
|
|
970
|
+
for (const review of data.reviews.slice(0, 3)) {
|
|
971
|
+
const reviewStars = 'ā
'.repeat(review.rating) + 'ā'.repeat(5 - review.rating);
|
|
972
|
+
console.log(` ${chalk_1.default.yellow(reviewStars)} "${review.review}"`);
|
|
973
|
+
console.log(` ${chalk_1.default.gray(`- ${review.reviewer_name}`)}`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
console.log(chalk_1.default.bold('\n Quick start:'));
|
|
977
|
+
console.log(chalk_1.default.cyan(` await client.send('${agentId}', 'Hello!')`));
|
|
978
|
+
console.log('');
|
|
979
|
+
}
|
|
980
|
+
catch (err) {
|
|
981
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
982
|
+
process.exit(1);
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
discover
|
|
986
|
+
.command('top')
|
|
987
|
+
.description(`Show top-rated agents
|
|
988
|
+
|
|
989
|
+
${chalk_1.default.bold('Example:')}
|
|
990
|
+
$ gopherhole discover top
|
|
991
|
+
$ gopherhole discover top --limit 5
|
|
992
|
+
`)
|
|
993
|
+
.option('-l, --limit <limit>', 'Number of results', '10')
|
|
994
|
+
.action(async (options) => {
|
|
995
|
+
const spinner = (0, ora_1.default)('Fetching top agents...').start();
|
|
996
|
+
try {
|
|
997
|
+
const res = await fetch(`${API_URL}/discover/agents?sort=rating&limit=${options.limit}`);
|
|
998
|
+
const data = await res.json();
|
|
999
|
+
spinner.stop();
|
|
1000
|
+
console.log(chalk_1.default.bold('\nš Top Rated Agents:\n'));
|
|
1001
|
+
let rank = 1;
|
|
1002
|
+
for (const agent of data.agents) {
|
|
1003
|
+
const stars = 'ā
'.repeat(Math.round(agent.avgRating)) + 'ā'.repeat(5 - Math.round(agent.avgRating));
|
|
1004
|
+
const medal = rank === 1 ? 'š„' : rank === 2 ? 'š„' : rank === 3 ? 'š„' : `#${rank}`;
|
|
1005
|
+
console.log(` ${medal} ${chalk_1.default.bold(agent.name)} ${chalk_1.default.yellow(stars)} (${agent.ratingCount})`);
|
|
1006
|
+
console.log(` ${chalk_1.default.gray(agent.description?.slice(0, 60) || 'No description')}${(agent.description?.length || 0) > 60 ? '...' : ''}`);
|
|
1007
|
+
console.log(` ${chalk_1.default.cyan(agent.id)}`);
|
|
1008
|
+
console.log('');
|
|
1009
|
+
rank++;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
catch (err) {
|
|
1013
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
1014
|
+
process.exit(1);
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
discover
|
|
1018
|
+
.command('rate <agentId>')
|
|
1019
|
+
.description(`Rate an agent
|
|
1020
|
+
|
|
1021
|
+
${chalk_1.default.bold('Example:')}
|
|
1022
|
+
$ gopherhole discover rate agent-abc123
|
|
1023
|
+
$ gopherhole discover rate agent-abc123 --rating 5 --message "Great bot!"
|
|
1024
|
+
`)
|
|
1025
|
+
.option('-r, --rating <rating>', 'Rating 1-5')
|
|
1026
|
+
.option('-m, --message <review>', 'Review message')
|
|
1027
|
+
.action(async (agentId, options) => {
|
|
1028
|
+
const sessionId = config.get('sessionId');
|
|
1029
|
+
if (!sessionId) {
|
|
1030
|
+
console.log(chalk_1.default.yellow('Not logged in.'));
|
|
1031
|
+
console.log(chalk_1.default.gray('Run: gopherhole login'));
|
|
1032
|
+
process.exit(1);
|
|
1033
|
+
}
|
|
1034
|
+
let { rating, message: review } = options;
|
|
1035
|
+
if (!rating) {
|
|
1036
|
+
const answers = await inquirer_1.default.prompt([
|
|
1037
|
+
{
|
|
1038
|
+
type: 'list',
|
|
1039
|
+
name: 'rating',
|
|
1040
|
+
message: 'Your rating:',
|
|
1041
|
+
choices: [
|
|
1042
|
+
{ name: 'ā
ā
ā
ā
ā
Excellent', value: 5 },
|
|
1043
|
+
{ name: 'ā
ā
ā
ā
ā Great', value: 4 },
|
|
1044
|
+
{ name: 'ā
ā
ā
āā Good', value: 3 },
|
|
1045
|
+
{ name: 'ā
ā
āāā Fair', value: 2 },
|
|
1046
|
+
{ name: 'ā
āāāā Poor', value: 1 },
|
|
1047
|
+
],
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
type: 'input',
|
|
1051
|
+
name: 'review',
|
|
1052
|
+
message: 'Review (optional):',
|
|
1053
|
+
},
|
|
1054
|
+
]);
|
|
1055
|
+
rating = answers.rating;
|
|
1056
|
+
review = answers.review;
|
|
1057
|
+
}
|
|
1058
|
+
const spinner = (0, ora_1.default)('Submitting rating...').start();
|
|
1059
|
+
try {
|
|
1060
|
+
const res = await fetch(`${API_URL}/discover/agents/${agentId}/rate`, {
|
|
1061
|
+
method: 'POST',
|
|
1062
|
+
headers: {
|
|
1063
|
+
'Content-Type': 'application/json',
|
|
1064
|
+
'X-Session-ID': sessionId,
|
|
1065
|
+
},
|
|
1066
|
+
body: JSON.stringify({
|
|
1067
|
+
rating: parseInt(rating),
|
|
1068
|
+
review: review || undefined,
|
|
1069
|
+
}),
|
|
1070
|
+
});
|
|
1071
|
+
if (!res.ok) {
|
|
1072
|
+
const err = await res.json();
|
|
1073
|
+
throw new Error(err.error || 'Failed to rate');
|
|
1074
|
+
}
|
|
1075
|
+
const data = await res.json();
|
|
1076
|
+
spinner.succeed(`Rated! New average: ${chalk_1.default.yellow(data.avgRating.toFixed(1))} (${data.ratingCount} reviews)`);
|
|
1077
|
+
}
|
|
1078
|
+
catch (err) {
|
|
1079
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
1080
|
+
process.exit(1);
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
// ========== STATUS COMMAND ==========
|
|
1084
|
+
program
|
|
1085
|
+
.command('status')
|
|
1086
|
+
.description('Show GopherHole service status and your session info')
|
|
1087
|
+
.action(async () => {
|
|
1088
|
+
console.log(chalk_1.default.bold('\nšæļø GopherHole Status\n'));
|
|
1089
|
+
// Check API health
|
|
1090
|
+
const spinner = (0, ora_1.default)('Checking API...').start();
|
|
1091
|
+
try {
|
|
1092
|
+
const start = Date.now();
|
|
1093
|
+
const res = await fetch(`${API_URL.replace('/api', '')}/health`);
|
|
1094
|
+
const latency = Date.now() - start;
|
|
1095
|
+
if (res.ok) {
|
|
1096
|
+
spinner.succeed(`API: ${brand.green('Online')} (${latency}ms)`);
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
spinner.fail(`API: ${chalk_1.default.red('Degraded')}`);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
catch {
|
|
1103
|
+
spinner.fail(`API: ${chalk_1.default.red('Offline')}`);
|
|
1104
|
+
}
|
|
1105
|
+
// Check login status
|
|
1106
|
+
const user = config.get('user');
|
|
1107
|
+
if (user) {
|
|
1108
|
+
console.log(` Auth: ${brand.green('Logged in')} as ${user.email}`);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
console.log(` Auth: ${chalk_1.default.yellow('Not logged in')}`);
|
|
1112
|
+
}
|
|
1113
|
+
console.log(` Config: ${config.path}`);
|
|
1114
|
+
console.log('');
|
|
1115
|
+
console.log(chalk_1.default.gray(' Dashboard: https://gopherhole.ai/dashboard'));
|
|
1116
|
+
console.log(chalk_1.default.gray(' Status: https://status.gopherhole.ai'));
|
|
1117
|
+
console.log('');
|
|
1118
|
+
});
|
|
395
1119
|
// Parse and run
|
|
396
1120
|
program.parse();
|