@gopherhole/cli 0.3.1 → 0.4.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 +13 -5
- package/dist/index.js +178 -1
- package/package.json +2 -2
- package/src/index.ts +169 -1
package/README.md
CHANGED
|
@@ -6,17 +6,17 @@ Connect AI agents to the world.
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Use with npx (no install required)
|
|
9
|
-
npx gopherhole init
|
|
9
|
+
npx @gopherhole/cli init
|
|
10
10
|
|
|
11
|
-
# Or install globally
|
|
12
|
-
npm install -g gopherhole
|
|
11
|
+
# Or install globally (exposes the `gopherhole` command)
|
|
12
|
+
npm install -g @gopherhole/cli
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
# Initialize a new agent in your project
|
|
19
|
-
npx gopherhole init
|
|
19
|
+
npx @gopherhole/cli init
|
|
20
20
|
|
|
21
21
|
# This will:
|
|
22
22
|
# 1. Log you in (or create an account)
|
|
@@ -64,12 +64,20 @@ The CLI stores configuration in:
|
|
|
64
64
|
- **Linux**: `~/.config/gopherhole-nodejs/`
|
|
65
65
|
- **Windows**: `%APPDATA%/gopherhole-nodejs/`
|
|
66
66
|
|
|
67
|
+
## Related Packages
|
|
68
|
+
|
|
69
|
+
- **[@gopherhole/mcp](https://www.npmjs.com/package/@gopherhole/mcp)** — MCP
|
|
70
|
+
server for Claude Code, Cursor, Windsurf, and other MCP-compatible IDEs.
|
|
71
|
+
Use this to call GopherHole agents from your IDE without writing any code.
|
|
72
|
+
- **[@gopherhole/sdk](https://www.npmjs.com/package/@gopherhole/sdk)** —
|
|
73
|
+
TypeScript SDK for building agents that send/receive A2A messages,
|
|
74
|
+
discover other agents, and use shared workspaces.
|
|
75
|
+
|
|
67
76
|
## Links
|
|
68
77
|
|
|
69
78
|
- Website: https://gopherhole.ai
|
|
70
79
|
- Dashboard: https://gopherhole.ai/dashboard
|
|
71
80
|
- Docs: https://docs.gopherhole.ai
|
|
72
|
-
- GitHub: https://github.com/gopherhole
|
|
73
81
|
|
|
74
82
|
## License
|
|
75
83
|
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ const brand = {
|
|
|
20
20
|
greenDark: chalk_1.default.hex('#16a34a'), // gopher-600 - emphasis
|
|
21
21
|
};
|
|
22
22
|
// Version
|
|
23
|
-
const VERSION = '0.
|
|
23
|
+
const VERSION = '0.4.1';
|
|
24
24
|
// ========== API KEY RESOLUTION ==========
|
|
25
25
|
// Precedence: --api-key flag > GOPHERHOLE_API_KEY env var > .env file in cwd
|
|
26
26
|
async function resolveApiKey(flagValue) {
|
|
@@ -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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gopherhole/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "GopherHole CLI - Connect AI agents to the world",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"author": "GopherHole",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@gopherhole/sdk": "^0.
|
|
25
|
+
"@gopherhole/sdk": "^0.7.2",
|
|
26
26
|
"chalk": "^5.3.0",
|
|
27
27
|
"commander": "^12.0.0",
|
|
28
28
|
"conf": "^12.0.0",
|
package/src/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ const brand = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
// Version
|
|
23
|
-
const VERSION = '0.
|
|
23
|
+
const VERSION = '0.4.1';
|
|
24
24
|
|
|
25
25
|
// ========== API KEY RESOLUTION ==========
|
|
26
26
|
// Precedence: --api-key flag > GOPHERHOLE_API_KEY env var > .env file in cwd
|
|
@@ -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
|