@gopherhole/cli 0.1.5 → 0.1.7

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.
Files changed (3) hide show
  1. package/dist/index.js +108 -17
  2. package/package.json +1 -1
  3. package/src/index.ts +111 -18
package/dist/index.js CHANGED
@@ -1236,7 +1236,11 @@ ${chalk_1.default.bold('Examples:')}
1236
1236
  access
1237
1237
  .command('list')
1238
1238
  .description('List access requests to your agents')
1239
- .option('--status <status>', 'Filter by status (pending, approved, rejected)', 'pending')
1239
+ .option('--agent <agentId>', 'Filter by agent ID')
1240
+ .option('--status <status>', 'Filter by status (pending, approved, rejected, all)', 'pending')
1241
+ .option('--search <query>', 'Search by requester name or ID')
1242
+ .option('--limit <n>', 'Max results (default 50)', '50')
1243
+ .option('--offset <n>', 'Skip first N results', '0')
1240
1244
  .option('--json', 'Output as JSON')
1241
1245
  .action(async (options) => {
1242
1246
  const sessionId = config.get('sessionId');
@@ -1246,9 +1250,19 @@ access
1246
1250
  process.exit(1);
1247
1251
  }
1248
1252
  const spinner = (0, ora_1.default)('Fetching access requests...').start();
1249
- log('GET /access/inbound');
1253
+ // Build query params
1254
+ const params = new URLSearchParams();
1255
+ if (options.agent)
1256
+ params.set('agent', options.agent);
1257
+ if (options.status && options.status !== 'all')
1258
+ params.set('status', options.status);
1259
+ if (options.search)
1260
+ params.set('search', options.search);
1261
+ params.set('limit', options.limit);
1262
+ params.set('offset', options.offset);
1263
+ log('GET /access/incoming?' + params.toString());
1250
1264
  try {
1251
- const res = await fetch(`${API_URL}/access/inbound`, {
1265
+ const res = await fetch(`${API_URL}/access/incoming?${params}`, {
1252
1266
  headers: { 'X-Session-ID': sessionId },
1253
1267
  });
1254
1268
  if (!res.ok) {
@@ -1256,34 +1270,44 @@ access
1256
1270
  }
1257
1271
  const data = await res.json();
1258
1272
  spinner.stop();
1259
- // Filter by status
1260
- const filtered = options.status === 'all'
1261
- ? data.grants
1262
- : data.grants.filter((g) => g.status === options.status);
1263
1273
  if (options.json) {
1264
- console.log(JSON.stringify(filtered, null, 2));
1274
+ console.log(JSON.stringify(data.grants, null, 2));
1265
1275
  return;
1266
1276
  }
1267
- if (filtered.length === 0) {
1268
- console.log(chalk_1.default.gray(`\nNo ${options.status} access requests.`));
1277
+ if (data.grants.length === 0) {
1278
+ console.log(chalk_1.default.gray(`\nNo ${options.status === 'all' ? '' : options.status + ' '}access requests found.`));
1279
+ if (options.agent)
1280
+ console.log(chalk_1.default.gray(` Agent filter: ${options.agent}`));
1281
+ if (options.search)
1282
+ console.log(chalk_1.default.gray(` Search: "${options.search}"`));
1269
1283
  return;
1270
1284
  }
1271
- console.log(chalk_1.default.bold(`\nšŸ“‹ ${options.status.charAt(0).toUpperCase() + options.status.slice(1)} Access Requests:\n`));
1272
- for (const grant of filtered) {
1285
+ console.log(chalk_1.default.bold(`\nšŸ“‹ Access Requests${options.agent ? ` for ${options.agent}` : ''}:\n`));
1286
+ for (const grant of data.grants) {
1273
1287
  const statusColor = grant.status === 'pending' ? chalk_1.default.yellow :
1274
1288
  grant.status === 'approved' ? chalk_1.default.green : chalk_1.default.red;
1275
1289
  console.log(` ${chalk_1.default.cyan(grant.id)}`);
1276
1290
  console.log(` From: ${grant.requester_agent_name || grant.requester_agent_id || 'Unknown'}`);
1277
1291
  console.log(` To: ${grant.target_agent_name || grant.target_agent_id}`);
1278
1292
  console.log(` Status: ${statusColor(grant.status)}`);
1293
+ if (grant.price_amount != null) {
1294
+ console.log(` Price: ${grant.price_amount} ${grant.price_currency}/${grant.price_unit}`);
1295
+ }
1296
+ if (grant.discount_percent != null) {
1297
+ console.log(` Discount: ${grant.discount_percent}%`);
1298
+ }
1279
1299
  if (grant.requested_reason) {
1280
1300
  console.log(` Reason: ${chalk_1.default.gray(grant.requested_reason)}`);
1281
1301
  }
1282
1302
  console.log(` Requested: ${new Date(grant.requested_at).toLocaleString()}`);
1283
1303
  console.log('');
1284
1304
  }
1285
- if (options.status === 'pending' && filtered.length > 0) {
1286
- console.log(chalk_1.default.gray(`Approve: gopherhole access approve <id>`));
1305
+ // Pagination info
1306
+ if (data.grants.length >= parseInt(options.limit)) {
1307
+ console.log(chalk_1.default.gray(`Showing ${data.grants.length} results. Use --offset ${parseInt(options.offset) + parseInt(options.limit)} for next page.`));
1308
+ }
1309
+ if (options.status === 'pending' && data.grants.length > 0) {
1310
+ console.log(chalk_1.default.gray(`\nApprove: gopherhole access approve <id>`));
1287
1311
  console.log(chalk_1.default.gray(`Reject: gopherhole access reject <id>`));
1288
1312
  }
1289
1313
  }
@@ -1295,10 +1319,11 @@ access
1295
1319
  access
1296
1320
  .command('approve <grantId>')
1297
1321
  .description('Approve an access request')
1298
- .option('--price <amount>', 'Set custom price for this requester (e.g., 0.01)')
1322
+ .option('--price <amount>', 'Set custom price (e.g., 0.01)')
1299
1323
  .option('--currency <code>', 'Currency code (default: USD)', 'USD')
1300
1324
  .option('--unit <unit>', 'Price unit (request, message, task, month)', 'request')
1301
- .option('--json', 'Output as JSON')
1325
+ .option('--discount <percent>', 'Discount off default price (e.g., 20 for 20% off)')
1326
+ .option('--skill-pricing <json>', 'Per-skill pricing as JSON (e.g., \'{"translate":{"amount":0.05,"currency":"USD","unit":"request"}}\')')
1302
1327
  .action(async (grantId, options) => {
1303
1328
  const sessionId = config.get('sessionId');
1304
1329
  if (!sessionId) {
@@ -1315,6 +1340,12 @@ access
1315
1340
  body.price_currency = options.currency;
1316
1341
  body.price_unit = options.unit;
1317
1342
  }
1343
+ if (options.discount) {
1344
+ body.discount_percent = parseFloat(options.discount);
1345
+ }
1346
+ if (options.skillPricing) {
1347
+ body.skill_pricing = JSON.parse(options.skillPricing);
1348
+ }
1318
1349
  const res = await fetch(`${API_URL}/access/${grantId}/approve`, {
1319
1350
  method: 'PUT',
1320
1351
  headers: {
@@ -1329,8 +1360,68 @@ access
1329
1360
  }
1330
1361
  spinner.succeed('Access request approved');
1331
1362
  if (options.price) {
1332
- console.log(chalk_1.default.gray(` Custom pricing: ${options.price} ${options.currency}/${options.unit}`));
1363
+ console.log(chalk_1.default.gray(` Custom price: ${options.price} ${options.currency}/${options.unit}`));
1364
+ }
1365
+ if (options.discount) {
1366
+ console.log(chalk_1.default.gray(` Discount: ${options.discount}% off`));
1367
+ }
1368
+ }
1369
+ catch (err) {
1370
+ spinner.fail(chalk_1.default.red(err.message));
1371
+ process.exit(1);
1372
+ }
1373
+ });
1374
+ access
1375
+ .command('edit <grantId>')
1376
+ .description('Edit pricing on an existing access grant')
1377
+ .option('--price <amount>', 'Set custom price (e.g., 0.01)')
1378
+ .option('--currency <code>', 'Currency code', 'USD')
1379
+ .option('--unit <unit>', 'Price unit (request, message, task, month)')
1380
+ .option('--discount <percent>', 'Discount off default price (e.g., 20 for 20% off)')
1381
+ .option('--clear-discount', 'Remove discount')
1382
+ .option('--skill-pricing <json>', 'Per-skill pricing as JSON')
1383
+ .option('--clear-skill-pricing', 'Remove per-skill pricing')
1384
+ .action(async (grantId, options) => {
1385
+ const sessionId = config.get('sessionId');
1386
+ if (!sessionId) {
1387
+ console.log(chalk_1.default.yellow('Not logged in.'));
1388
+ console.log(chalk_1.default.gray('Run: gopherhole login'));
1389
+ process.exit(1);
1390
+ }
1391
+ const spinner = (0, ora_1.default)('Updating access grant...').start();
1392
+ log('PATCH /access/' + grantId);
1393
+ try {
1394
+ const body = {};
1395
+ if (options.price) {
1396
+ body.price_amount = parseFloat(options.price);
1397
+ body.price_currency = options.currency;
1398
+ body.price_unit = options.unit;
1399
+ }
1400
+ if (options.discount) {
1401
+ body.discount_percent = parseFloat(options.discount);
1402
+ }
1403
+ if (options.clearDiscount) {
1404
+ body.discount_percent = null;
1405
+ }
1406
+ if (options.skillPricing) {
1407
+ body.skill_pricing = JSON.parse(options.skillPricing);
1408
+ }
1409
+ if (options.clearSkillPricing) {
1410
+ body.skill_pricing = null;
1411
+ }
1412
+ const res = await fetch(`${API_URL}/access/${grantId}`, {
1413
+ method: 'PATCH',
1414
+ headers: {
1415
+ 'Content-Type': 'application/json',
1416
+ 'X-Session-ID': sessionId,
1417
+ },
1418
+ body: JSON.stringify(body),
1419
+ });
1420
+ if (!res.ok) {
1421
+ const err = await res.json();
1422
+ throw new Error(err.error || 'Failed to update grant');
1333
1423
  }
1424
+ spinner.succeed('Access grant updated');
1334
1425
  }
1335
1426
  catch (err) {
1336
1427
  spinner.fail(chalk_1.default.red(err.message));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gopherhole/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "GopherHole CLI - Connect AI agents to the world",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -1375,7 +1375,11 @@ ${chalk.bold('Examples:')}
1375
1375
  access
1376
1376
  .command('list')
1377
1377
  .description('List access requests to your agents')
1378
- .option('--status <status>', 'Filter by status (pending, approved, rejected)', 'pending')
1378
+ .option('--agent <agentId>', 'Filter by agent ID')
1379
+ .option('--status <status>', 'Filter by status (pending, approved, rejected, all)', 'pending')
1380
+ .option('--search <query>', 'Search by requester name or ID')
1381
+ .option('--limit <n>', 'Max results (default 50)', '50')
1382
+ .option('--offset <n>', 'Skip first N results', '0')
1379
1383
  .option('--json', 'Output as JSON')
1380
1384
  .action(async (options) => {
1381
1385
  const sessionId = config.get('sessionId') as string;
@@ -1386,10 +1390,19 @@ access
1386
1390
  }
1387
1391
 
1388
1392
  const spinner = ora('Fetching access requests...').start();
1389
- log('GET /access/inbound');
1393
+
1394
+ // Build query params
1395
+ const params = new URLSearchParams();
1396
+ if (options.agent) params.set('agent', options.agent);
1397
+ if (options.status && options.status !== 'all') params.set('status', options.status);
1398
+ if (options.search) params.set('search', options.search);
1399
+ params.set('limit', options.limit);
1400
+ params.set('offset', options.offset);
1401
+
1402
+ log('GET /access/incoming?' + params.toString());
1390
1403
 
1391
1404
  try {
1392
- const res = await fetch(`${API_URL}/access/inbound`, {
1405
+ const res = await fetch(`${API_URL}/access/incoming?${params}`, {
1393
1406
  headers: { 'X-Session-ID': sessionId },
1394
1407
  });
1395
1408
 
@@ -1400,30 +1413,33 @@ access
1400
1413
  const data = await res.json();
1401
1414
  spinner.stop();
1402
1415
 
1403
- // Filter by status
1404
- const filtered = options.status === 'all'
1405
- ? data.grants
1406
- : data.grants.filter((g: any) => g.status === options.status);
1407
-
1408
1416
  if (options.json) {
1409
- console.log(JSON.stringify(filtered, null, 2));
1417
+ console.log(JSON.stringify(data.grants, null, 2));
1410
1418
  return;
1411
1419
  }
1412
1420
 
1413
- if (filtered.length === 0) {
1414
- console.log(chalk.gray(`\nNo ${options.status} access requests.`));
1421
+ if (data.grants.length === 0) {
1422
+ console.log(chalk.gray(`\nNo ${options.status === 'all' ? '' : options.status + ' '}access requests found.`));
1423
+ if (options.agent) console.log(chalk.gray(` Agent filter: ${options.agent}`));
1424
+ if (options.search) console.log(chalk.gray(` Search: "${options.search}"`));
1415
1425
  return;
1416
1426
  }
1417
1427
 
1418
- console.log(chalk.bold(`\nšŸ“‹ ${options.status.charAt(0).toUpperCase() + options.status.slice(1)} Access Requests:\n`));
1428
+ console.log(chalk.bold(`\nšŸ“‹ Access Requests${options.agent ? ` for ${options.agent}` : ''}:\n`));
1419
1429
 
1420
- for (const grant of filtered) {
1430
+ for (const grant of data.grants) {
1421
1431
  const statusColor = grant.status === 'pending' ? chalk.yellow :
1422
1432
  grant.status === 'approved' ? chalk.green : chalk.red;
1423
1433
  console.log(` ${chalk.cyan(grant.id)}`);
1424
1434
  console.log(` From: ${grant.requester_agent_name || grant.requester_agent_id || 'Unknown'}`);
1425
1435
  console.log(` To: ${grant.target_agent_name || grant.target_agent_id}`);
1426
1436
  console.log(` Status: ${statusColor(grant.status)}`);
1437
+ if (grant.price_amount != null) {
1438
+ console.log(` Price: ${grant.price_amount} ${grant.price_currency}/${grant.price_unit}`);
1439
+ }
1440
+ if (grant.discount_percent != null) {
1441
+ console.log(` Discount: ${grant.discount_percent}%`);
1442
+ }
1427
1443
  if (grant.requested_reason) {
1428
1444
  console.log(` Reason: ${chalk.gray(grant.requested_reason)}`);
1429
1445
  }
@@ -1431,8 +1447,13 @@ access
1431
1447
  console.log('');
1432
1448
  }
1433
1449
 
1434
- if (options.status === 'pending' && filtered.length > 0) {
1435
- console.log(chalk.gray(`Approve: gopherhole access approve <id>`));
1450
+ // Pagination info
1451
+ if (data.grants.length >= parseInt(options.limit)) {
1452
+ console.log(chalk.gray(`Showing ${data.grants.length} results. Use --offset ${parseInt(options.offset) + parseInt(options.limit)} for next page.`));
1453
+ }
1454
+
1455
+ if (options.status === 'pending' && data.grants.length > 0) {
1456
+ console.log(chalk.gray(`\nApprove: gopherhole access approve <id>`));
1436
1457
  console.log(chalk.gray(`Reject: gopherhole access reject <id>`));
1437
1458
  }
1438
1459
  } catch (err) {
@@ -1444,10 +1465,11 @@ access
1444
1465
  access
1445
1466
  .command('approve <grantId>')
1446
1467
  .description('Approve an access request')
1447
- .option('--price <amount>', 'Set custom price for this requester (e.g., 0.01)')
1468
+ .option('--price <amount>', 'Set custom price (e.g., 0.01)')
1448
1469
  .option('--currency <code>', 'Currency code (default: USD)', 'USD')
1449
1470
  .option('--unit <unit>', 'Price unit (request, message, task, month)', 'request')
1450
- .option('--json', 'Output as JSON')
1471
+ .option('--discount <percent>', 'Discount off default price (e.g., 20 for 20% off)')
1472
+ .option('--skill-pricing <json>', 'Per-skill pricing as JSON (e.g., \'{"translate":{"amount":0.05,"currency":"USD","unit":"request"}}\')')
1451
1473
  .action(async (grantId, options) => {
1452
1474
  const sessionId = config.get('sessionId') as string;
1453
1475
  if (!sessionId) {
@@ -1466,6 +1488,12 @@ access
1466
1488
  body.price_currency = options.currency;
1467
1489
  body.price_unit = options.unit;
1468
1490
  }
1491
+ if (options.discount) {
1492
+ body.discount_percent = parseFloat(options.discount);
1493
+ }
1494
+ if (options.skillPricing) {
1495
+ body.skill_pricing = JSON.parse(options.skillPricing);
1496
+ }
1469
1497
 
1470
1498
  const res = await fetch(`${API_URL}/access/${grantId}/approve`, {
1471
1499
  method: 'PUT',
@@ -1484,8 +1512,73 @@ access
1484
1512
  spinner.succeed('Access request approved');
1485
1513
 
1486
1514
  if (options.price) {
1487
- console.log(chalk.gray(` Custom pricing: ${options.price} ${options.currency}/${options.unit}`));
1515
+ console.log(chalk.gray(` Custom price: ${options.price} ${options.currency}/${options.unit}`));
1488
1516
  }
1517
+ if (options.discount) {
1518
+ console.log(chalk.gray(` Discount: ${options.discount}% off`));
1519
+ }
1520
+ } catch (err) {
1521
+ spinner.fail(chalk.red((err as Error).message));
1522
+ process.exit(1);
1523
+ }
1524
+ });
1525
+
1526
+ access
1527
+ .command('edit <grantId>')
1528
+ .description('Edit pricing on an existing access grant')
1529
+ .option('--price <amount>', 'Set custom price (e.g., 0.01)')
1530
+ .option('--currency <code>', 'Currency code', 'USD')
1531
+ .option('--unit <unit>', 'Price unit (request, message, task, month)')
1532
+ .option('--discount <percent>', 'Discount off default price (e.g., 20 for 20% off)')
1533
+ .option('--clear-discount', 'Remove discount')
1534
+ .option('--skill-pricing <json>', 'Per-skill pricing as JSON')
1535
+ .option('--clear-skill-pricing', 'Remove per-skill pricing')
1536
+ .action(async (grantId, options) => {
1537
+ const sessionId = config.get('sessionId') as string;
1538
+ if (!sessionId) {
1539
+ console.log(chalk.yellow('Not logged in.'));
1540
+ console.log(chalk.gray('Run: gopherhole login'));
1541
+ process.exit(1);
1542
+ }
1543
+
1544
+ const spinner = ora('Updating access grant...').start();
1545
+ log('PATCH /access/' + grantId);
1546
+
1547
+ try {
1548
+ const body: any = {};
1549
+ if (options.price) {
1550
+ body.price_amount = parseFloat(options.price);
1551
+ body.price_currency = options.currency;
1552
+ body.price_unit = options.unit;
1553
+ }
1554
+ if (options.discount) {
1555
+ body.discount_percent = parseFloat(options.discount);
1556
+ }
1557
+ if (options.clearDiscount) {
1558
+ body.discount_percent = null;
1559
+ }
1560
+ if (options.skillPricing) {
1561
+ body.skill_pricing = JSON.parse(options.skillPricing);
1562
+ }
1563
+ if (options.clearSkillPricing) {
1564
+ body.skill_pricing = null;
1565
+ }
1566
+
1567
+ const res = await fetch(`${API_URL}/access/${grantId}`, {
1568
+ method: 'PATCH',
1569
+ headers: {
1570
+ 'Content-Type': 'application/json',
1571
+ 'X-Session-ID': sessionId,
1572
+ },
1573
+ body: JSON.stringify(body),
1574
+ });
1575
+
1576
+ if (!res.ok) {
1577
+ const err = await res.json();
1578
+ throw new Error(err.error || 'Failed to update grant');
1579
+ }
1580
+
1581
+ spinner.succeed('Access grant updated');
1489
1582
  } catch (err) {
1490
1583
  spinner.fail(chalk.red((err as Error).message));
1491
1584
  process.exit(1);