@gopherhole/cli 0.3.1 → 0.4.0
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 +177 -0
- package/package.json +1 -1
- package/src/index.ts +168 -0
package/dist/index.js
CHANGED
|
@@ -1274,6 +1274,183 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
1274
1274
|
process.exit(1);
|
|
1275
1275
|
}
|
|
1276
1276
|
});
|
|
1277
|
+
// ========== TASK COMMANDS ==========
|
|
1278
|
+
const taskCmd = program
|
|
1279
|
+
.command('task')
|
|
1280
|
+
.description(`Manage tasks (queued messages, pending responses)
|
|
1281
|
+
|
|
1282
|
+
${chalk_1.default.bold('Examples:')}
|
|
1283
|
+
$ gopherhole task status task-abc123
|
|
1284
|
+
$ gopherhole task pending
|
|
1285
|
+
$ gopherhole task cancel task-abc123
|
|
1286
|
+
$ gopherhole task cancel-all
|
|
1287
|
+
`);
|
|
1288
|
+
taskCmd
|
|
1289
|
+
.command('status <taskId>')
|
|
1290
|
+
.description('Check the status of a task and get the response if completed')
|
|
1291
|
+
.action(async (taskId) => {
|
|
1292
|
+
const sessionId = config.get('sessionId');
|
|
1293
|
+
if (!sessionId) {
|
|
1294
|
+
console.log(chalk_1.default.yellow('Not logged in. Run: gopherhole login'));
|
|
1295
|
+
process.exit(1);
|
|
1296
|
+
}
|
|
1297
|
+
const spinner = (0, ora_1.default)(`Checking task ${taskId}...`).start();
|
|
1298
|
+
try {
|
|
1299
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1300
|
+
method: 'POST',
|
|
1301
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1302
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'GetTask', params: { id: taskId }, id: 1 }),
|
|
1303
|
+
});
|
|
1304
|
+
const data = await res.json();
|
|
1305
|
+
if (data.error)
|
|
1306
|
+
throw new Error(data.error.message || 'Failed');
|
|
1307
|
+
const task = data.result;
|
|
1308
|
+
const state = task?.status?.state || 'unknown';
|
|
1309
|
+
spinner.stop();
|
|
1310
|
+
console.log(`\n${chalk_1.default.bold('Task:')} ${taskId}`);
|
|
1311
|
+
console.log(`${chalk_1.default.bold('State:')} ${stateColor(state)(state)}`);
|
|
1312
|
+
if (task?.status?.timestamp)
|
|
1313
|
+
console.log(`${chalk_1.default.bold('Time:')} ${task.status.timestamp}`);
|
|
1314
|
+
if (state === 'completed' && task?.artifacts?.length) {
|
|
1315
|
+
const texts = task.artifacts.flatMap((a) => a.parts?.filter((p) => p.kind === 'text').map((p) => p.text) || []);
|
|
1316
|
+
if (texts.length)
|
|
1317
|
+
console.log(`\n${chalk_1.default.bold('Response:')}\n${texts.join('\n')}`);
|
|
1318
|
+
}
|
|
1319
|
+
else if (state === 'submitted') {
|
|
1320
|
+
console.log(chalk_1.default.yellow('\n⏳ Queued — recipient hasn\'t come online yet.'));
|
|
1321
|
+
}
|
|
1322
|
+
else if (state === 'working') {
|
|
1323
|
+
console.log(chalk_1.default.blue('\n⚙️ Delivered — waiting for response.'));
|
|
1324
|
+
}
|
|
1325
|
+
else if (state === 'failed') {
|
|
1326
|
+
const msg = task?.status?.message;
|
|
1327
|
+
console.log(chalk_1.default.red(`\n❌ ${typeof msg === 'string' ? msg : 'Unknown error'}`));
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
catch (err) {
|
|
1331
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
1332
|
+
process.exit(1);
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
taskCmd
|
|
1336
|
+
.command('pending')
|
|
1337
|
+
.description('List all queued/pending tasks')
|
|
1338
|
+
.option('-l, --limit <n>', 'Max tasks to show', parseInt, 20)
|
|
1339
|
+
.action(async (opts) => {
|
|
1340
|
+
const sessionId = config.get('sessionId');
|
|
1341
|
+
if (!sessionId) {
|
|
1342
|
+
console.log(chalk_1.default.yellow('Not logged in. Run: gopherhole login'));
|
|
1343
|
+
process.exit(1);
|
|
1344
|
+
}
|
|
1345
|
+
const spinner = (0, ora_1.default)('Fetching pending tasks...').start();
|
|
1346
|
+
try {
|
|
1347
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1348
|
+
method: 'POST',
|
|
1349
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1350
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'ListTasks', params: { status: 'submitted', pageSize: opts.limit || 20 }, id: 1 }),
|
|
1351
|
+
});
|
|
1352
|
+
const data = await res.json();
|
|
1353
|
+
if (data.error)
|
|
1354
|
+
throw new Error(data.error.message || 'Failed');
|
|
1355
|
+
const tasks = data.result?.tasks || [];
|
|
1356
|
+
spinner.stop();
|
|
1357
|
+
if (tasks.length === 0) {
|
|
1358
|
+
console.log(chalk_1.default.green('✅ No pending tasks.'));
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
console.log(chalk_1.default.bold(`\n${tasks.length} pending task(s):\n`));
|
|
1362
|
+
for (const t of tasks) {
|
|
1363
|
+
const age = Date.now() - new Date(t.status?.timestamp || 0).getTime();
|
|
1364
|
+
const ageMins = Math.round(age / 60000);
|
|
1365
|
+
console.log(` ${chalk_1.default.gray(t.id)} → ${t.serverAgentId || 'unknown'} ${chalk_1.default.yellow(`(${ageMins}m ago)`)}`);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
catch (err) {
|
|
1369
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
1370
|
+
process.exit(1);
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
taskCmd
|
|
1374
|
+
.command('cancel <taskId>')
|
|
1375
|
+
.description('Cancel a specific task and purge its queued messages')
|
|
1376
|
+
.action(async (taskId) => {
|
|
1377
|
+
const sessionId = config.get('sessionId');
|
|
1378
|
+
if (!sessionId) {
|
|
1379
|
+
console.log(chalk_1.default.yellow('Not logged in. Run: gopherhole login'));
|
|
1380
|
+
process.exit(1);
|
|
1381
|
+
}
|
|
1382
|
+
const spinner = (0, ora_1.default)(`Canceling task ${taskId}...`).start();
|
|
1383
|
+
try {
|
|
1384
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1385
|
+
method: 'POST',
|
|
1386
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1387
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'CancelTask', params: { id: taskId }, id: 1 }),
|
|
1388
|
+
});
|
|
1389
|
+
const data = await res.json();
|
|
1390
|
+
if (data.error)
|
|
1391
|
+
throw new Error(data.error.message || 'Failed');
|
|
1392
|
+
spinner.succeed(`Task ${taskId} canceled. Queued messages purged.`);
|
|
1393
|
+
}
|
|
1394
|
+
catch (err) {
|
|
1395
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
1396
|
+
process.exit(1);
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
taskCmd
|
|
1400
|
+
.command('cancel-all')
|
|
1401
|
+
.description('Cancel ALL pending tasks and purge all queued messages')
|
|
1402
|
+
.action(async () => {
|
|
1403
|
+
const sessionId = config.get('sessionId');
|
|
1404
|
+
if (!sessionId) {
|
|
1405
|
+
console.log(chalk_1.default.yellow('Not logged in. Run: gopherhole login'));
|
|
1406
|
+
process.exit(1);
|
|
1407
|
+
}
|
|
1408
|
+
const spinner = (0, ora_1.default)('Fetching pending tasks...').start();
|
|
1409
|
+
try {
|
|
1410
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1411
|
+
method: 'POST',
|
|
1412
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1413
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'ListTasks', params: { status: 'submitted', pageSize: 100 }, id: 1 }),
|
|
1414
|
+
});
|
|
1415
|
+
const data = await res.json();
|
|
1416
|
+
if (data.error)
|
|
1417
|
+
throw new Error(data.error.message || 'Failed');
|
|
1418
|
+
const tasks = data.result?.tasks || [];
|
|
1419
|
+
if (tasks.length === 0) {
|
|
1420
|
+
spinner.succeed('No pending tasks to cancel.');
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
spinner.text = `Canceling ${tasks.length} task(s)...`;
|
|
1424
|
+
let canceled = 0;
|
|
1425
|
+
for (const t of tasks) {
|
|
1426
|
+
try {
|
|
1427
|
+
await fetch(`${API_URL}/../a2a`, {
|
|
1428
|
+
method: 'POST',
|
|
1429
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1430
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'CancelTask', params: { id: t.id }, id: 1 }),
|
|
1431
|
+
});
|
|
1432
|
+
canceled++;
|
|
1433
|
+
}
|
|
1434
|
+
catch { /* skip */ }
|
|
1435
|
+
}
|
|
1436
|
+
spinner.succeed(`Canceled ${canceled}/${tasks.length} task(s). Queued messages purged.`);
|
|
1437
|
+
}
|
|
1438
|
+
catch (err) {
|
|
1439
|
+
spinner.fail(chalk_1.default.red(err.message));
|
|
1440
|
+
process.exit(1);
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
function stateColor(state) {
|
|
1444
|
+
switch (state) {
|
|
1445
|
+
case 'completed': return chalk_1.default.green;
|
|
1446
|
+
case 'submitted': return chalk_1.default.yellow;
|
|
1447
|
+
case 'working': return chalk_1.default.blue;
|
|
1448
|
+
case 'failed':
|
|
1449
|
+
case 'canceled':
|
|
1450
|
+
case 'rejected': return chalk_1.default.red;
|
|
1451
|
+
default: return chalk_1.default.gray;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1277
1454
|
// ========== DISCOVER COMMANDS ==========
|
|
1278
1455
|
const discover = program
|
|
1279
1456
|
.command('discover')
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1412,6 +1412,174 @@ ${chalk.bold('Examples:')}
|
|
|
1412
1412
|
}
|
|
1413
1413
|
});
|
|
1414
1414
|
|
|
1415
|
+
// ========== TASK COMMANDS ==========
|
|
1416
|
+
|
|
1417
|
+
const taskCmd = program
|
|
1418
|
+
.command('task')
|
|
1419
|
+
.description(`Manage tasks (queued messages, pending responses)
|
|
1420
|
+
|
|
1421
|
+
${chalk.bold('Examples:')}
|
|
1422
|
+
$ gopherhole task status task-abc123
|
|
1423
|
+
$ gopherhole task pending
|
|
1424
|
+
$ gopherhole task cancel task-abc123
|
|
1425
|
+
$ gopherhole task cancel-all
|
|
1426
|
+
`);
|
|
1427
|
+
|
|
1428
|
+
taskCmd
|
|
1429
|
+
.command('status <taskId>')
|
|
1430
|
+
.description('Check the status of a task and get the response if completed')
|
|
1431
|
+
.action(async (taskId) => {
|
|
1432
|
+
const sessionId = config.get('sessionId') as string;
|
|
1433
|
+
if (!sessionId) { console.log(chalk.yellow('Not logged in. Run: gopherhole login')); process.exit(1); }
|
|
1434
|
+
|
|
1435
|
+
const spinner = ora(`Checking task ${taskId}...`).start();
|
|
1436
|
+
try {
|
|
1437
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1438
|
+
method: 'POST',
|
|
1439
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1440
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'GetTask', params: { id: taskId }, id: 1 }),
|
|
1441
|
+
});
|
|
1442
|
+
const data = await res.json() as any;
|
|
1443
|
+
if (data.error) throw new Error(data.error.message || 'Failed');
|
|
1444
|
+
|
|
1445
|
+
const task = data.result;
|
|
1446
|
+
const state = task?.status?.state || 'unknown';
|
|
1447
|
+
spinner.stop();
|
|
1448
|
+
|
|
1449
|
+
console.log(`\n${chalk.bold('Task:')} ${taskId}`);
|
|
1450
|
+
console.log(`${chalk.bold('State:')} ${stateColor(state)(state)}`);
|
|
1451
|
+
if (task?.status?.timestamp) console.log(`${chalk.bold('Time:')} ${task.status.timestamp}`);
|
|
1452
|
+
|
|
1453
|
+
if (state === 'completed' && task?.artifacts?.length) {
|
|
1454
|
+
const texts = task.artifacts.flatMap((a: any) => a.parts?.filter((p: any) => p.kind === 'text').map((p: any) => p.text) || []);
|
|
1455
|
+
if (texts.length) console.log(`\n${chalk.bold('Response:')}\n${texts.join('\n')}`);
|
|
1456
|
+
} else if (state === 'submitted') {
|
|
1457
|
+
console.log(chalk.yellow('\n⏳ Queued — recipient hasn\'t come online yet.'));
|
|
1458
|
+
} else if (state === 'working') {
|
|
1459
|
+
console.log(chalk.blue('\n⚙️ Delivered — waiting for response.'));
|
|
1460
|
+
} else if (state === 'failed') {
|
|
1461
|
+
const msg = task?.status?.message;
|
|
1462
|
+
console.log(chalk.red(`\n❌ ${typeof msg === 'string' ? msg : 'Unknown error'}`));
|
|
1463
|
+
}
|
|
1464
|
+
} catch (err) {
|
|
1465
|
+
spinner.fail(chalk.red((err as Error).message));
|
|
1466
|
+
process.exit(1);
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
taskCmd
|
|
1471
|
+
.command('pending')
|
|
1472
|
+
.description('List all queued/pending tasks')
|
|
1473
|
+
.option('-l, --limit <n>', 'Max tasks to show', parseInt, 20)
|
|
1474
|
+
.action(async (opts) => {
|
|
1475
|
+
const sessionId = config.get('sessionId') as string;
|
|
1476
|
+
if (!sessionId) { console.log(chalk.yellow('Not logged in. Run: gopherhole login')); process.exit(1); }
|
|
1477
|
+
|
|
1478
|
+
const spinner = ora('Fetching pending tasks...').start();
|
|
1479
|
+
try {
|
|
1480
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1481
|
+
method: 'POST',
|
|
1482
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1483
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'ListTasks', params: { status: 'submitted', pageSize: opts.limit || 20 }, id: 1 }),
|
|
1484
|
+
});
|
|
1485
|
+
const data = await res.json() as any;
|
|
1486
|
+
if (data.error) throw new Error(data.error.message || 'Failed');
|
|
1487
|
+
|
|
1488
|
+
const tasks = data.result?.tasks || [];
|
|
1489
|
+
spinner.stop();
|
|
1490
|
+
|
|
1491
|
+
if (tasks.length === 0) {
|
|
1492
|
+
console.log(chalk.green('✅ No pending tasks.'));
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
console.log(chalk.bold(`\n${tasks.length} pending task(s):\n`));
|
|
1497
|
+
for (const t of tasks) {
|
|
1498
|
+
const age = Date.now() - new Date(t.status?.timestamp || 0).getTime();
|
|
1499
|
+
const ageMins = Math.round(age / 60000);
|
|
1500
|
+
console.log(` ${chalk.gray(t.id)} → ${t.serverAgentId || 'unknown'} ${chalk.yellow(`(${ageMins}m ago)`)}`);
|
|
1501
|
+
}
|
|
1502
|
+
} catch (err) {
|
|
1503
|
+
spinner.fail(chalk.red((err as Error).message));
|
|
1504
|
+
process.exit(1);
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
taskCmd
|
|
1509
|
+
.command('cancel <taskId>')
|
|
1510
|
+
.description('Cancel a specific task and purge its queued messages')
|
|
1511
|
+
.action(async (taskId) => {
|
|
1512
|
+
const sessionId = config.get('sessionId') as string;
|
|
1513
|
+
if (!sessionId) { console.log(chalk.yellow('Not logged in. Run: gopherhole login')); process.exit(1); }
|
|
1514
|
+
|
|
1515
|
+
const spinner = ora(`Canceling task ${taskId}...`).start();
|
|
1516
|
+
try {
|
|
1517
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1518
|
+
method: 'POST',
|
|
1519
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1520
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'CancelTask', params: { id: taskId }, id: 1 }),
|
|
1521
|
+
});
|
|
1522
|
+
const data = await res.json() as any;
|
|
1523
|
+
if (data.error) throw new Error(data.error.message || 'Failed');
|
|
1524
|
+
spinner.succeed(`Task ${taskId} canceled. Queued messages purged.`);
|
|
1525
|
+
} catch (err) {
|
|
1526
|
+
spinner.fail(chalk.red((err as Error).message));
|
|
1527
|
+
process.exit(1);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
taskCmd
|
|
1532
|
+
.command('cancel-all')
|
|
1533
|
+
.description('Cancel ALL pending tasks and purge all queued messages')
|
|
1534
|
+
.action(async () => {
|
|
1535
|
+
const sessionId = config.get('sessionId') as string;
|
|
1536
|
+
if (!sessionId) { console.log(chalk.yellow('Not logged in. Run: gopherhole login')); process.exit(1); }
|
|
1537
|
+
|
|
1538
|
+
const spinner = ora('Fetching pending tasks...').start();
|
|
1539
|
+
try {
|
|
1540
|
+
const res = await fetch(`${API_URL}/../a2a`, {
|
|
1541
|
+
method: 'POST',
|
|
1542
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1543
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'ListTasks', params: { status: 'submitted', pageSize: 100 }, id: 1 }),
|
|
1544
|
+
});
|
|
1545
|
+
const data = await res.json() as any;
|
|
1546
|
+
if (data.error) throw new Error(data.error.message || 'Failed');
|
|
1547
|
+
|
|
1548
|
+
const tasks = data.result?.tasks || [];
|
|
1549
|
+
if (tasks.length === 0) {
|
|
1550
|
+
spinner.succeed('No pending tasks to cancel.');
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
spinner.text = `Canceling ${tasks.length} task(s)...`;
|
|
1555
|
+
let canceled = 0;
|
|
1556
|
+
for (const t of tasks) {
|
|
1557
|
+
try {
|
|
1558
|
+
await fetch(`${API_URL}/../a2a`, {
|
|
1559
|
+
method: 'POST',
|
|
1560
|
+
headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId },
|
|
1561
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'CancelTask', params: { id: t.id }, id: 1 }),
|
|
1562
|
+
});
|
|
1563
|
+
canceled++;
|
|
1564
|
+
} catch { /* skip */ }
|
|
1565
|
+
}
|
|
1566
|
+
spinner.succeed(`Canceled ${canceled}/${tasks.length} task(s). Queued messages purged.`);
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
spinner.fail(chalk.red((err as Error).message));
|
|
1569
|
+
process.exit(1);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
|
|
1573
|
+
function stateColor(state: string) {
|
|
1574
|
+
switch (state) {
|
|
1575
|
+
case 'completed': return chalk.green;
|
|
1576
|
+
case 'submitted': return chalk.yellow;
|
|
1577
|
+
case 'working': return chalk.blue;
|
|
1578
|
+
case 'failed': case 'canceled': case 'rejected': return chalk.red;
|
|
1579
|
+
default: return chalk.gray;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1415
1583
|
// ========== DISCOVER COMMANDS ==========
|
|
1416
1584
|
|
|
1417
1585
|
const discover = program
|