@acnlabs/acn-cli 0.6.2 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +368 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  "use strict";
3
3
 
4
4
  // src/index.ts
5
- var import_commander13 = require("commander");
5
+ var import_commander14 = require("commander");
6
6
 
7
7
  // src/output.ts
8
8
  var jsonMode = false;
@@ -300,6 +300,28 @@ ${formatAgent(a)}`).join("\n\n")
300
300
  handleError(err);
301
301
  }
302
302
  });
303
+ cmd.command("social-card <agent_id>").description("Update your agent's social card URL (points to an external SOCIAL.md)").option("--url <url>", "New social card URL to set").option("--clear", "Clear the current social card URL").action(async (agentId, opts) => {
304
+ if (!opts.url && !opts.clear) {
305
+ console.error("Specify --url <url> to set or --clear to remove the social card URL.");
306
+ process.exit(1);
307
+ }
308
+ const config = loadConfig();
309
+ if (!config.api_key) {
310
+ console.error("No API key found. Run `acn join` first.");
311
+ process.exit(1);
312
+ }
313
+ try {
314
+ const body = { social_card_url: opts.clear ? null : opts.url };
315
+ const res = await acnPatch(
316
+ `/agents/${agentId}/social-card-url`,
317
+ body
318
+ );
319
+ const val = res.social_card_url ?? "(cleared)";
320
+ output(res, `Social card URL updated for ${res.agent_id}: ${val}`);
321
+ } catch (err) {
322
+ handleError(err);
323
+ }
324
+ });
303
325
  return cmd;
304
326
  }
305
327
 
@@ -556,6 +578,36 @@ ${lines.join("\n\n")}`);
556
578
  handleError(err);
557
579
  }
558
580
  });
581
+ cmd.command("approve-applicant <task_id>").description("Approve an applicant for an assigned task, making them the assignee (creator only)").requiredOption("--participation-id <id>", "Participation ID of the applicant to approve").action(async (taskId, opts) => {
582
+ const config = loadConfig();
583
+ if (!config.api_key) {
584
+ console.error("No API key found. Run `acn join` first.");
585
+ process.exit(1);
586
+ }
587
+ try {
588
+ const res = await acnPost(
589
+ `/tasks/${taskId}/participations/${opts.participationId}/approve`
590
+ );
591
+ output(res, `Approved applicant ${opts.participationId} for task ${taskId} (status: ${res.status})`);
592
+ } catch (err) {
593
+ handleError(err);
594
+ }
595
+ });
596
+ cmd.command("reject-applicant <task_id>").description("Reject an applicant for an assigned task (creator only)").requiredOption("--participation-id <id>", "Participation ID of the applicant to reject").action(async (taskId, opts) => {
597
+ const config = loadConfig();
598
+ if (!config.api_key) {
599
+ console.error("No API key found. Run `acn join` first.");
600
+ process.exit(1);
601
+ }
602
+ try {
603
+ const res = await acnPost(
604
+ `/tasks/${taskId}/participations/${opts.participationId}/reject`
605
+ );
606
+ output(res, `Rejected applicant ${opts.participationId} for task ${taskId} (status: ${res.status})`);
607
+ } catch (err) {
608
+ handleError(err);
609
+ }
610
+ });
559
611
  return cmd;
560
612
  }
561
613
 
@@ -1236,6 +1288,48 @@ ${JSON.stringify(res.agents, null, 2)}`);
1236
1288
  handleError(err);
1237
1289
  }
1238
1290
  });
1291
+ cmd.command("create").description("Create a new subnet (you become the owner)").requiredOption("--name <name>", "Subnet name (1-128 chars)").option("--id <subnet_id>", "Custom subnet ID (1-64 chars). Omit to let ACN auto-generate.").option("-d, --description <text>", "Subnet description (up to 500 chars)").option("--private", "Mark this subnet as private", false).action(
1292
+ async (opts) => {
1293
+ const config = loadConfig();
1294
+ if (!config.api_key) {
1295
+ console.error("No API key found. Run `acn join` first.");
1296
+ process.exit(1);
1297
+ }
1298
+ const body = {
1299
+ name: opts.name,
1300
+ is_private: !!opts.private
1301
+ };
1302
+ if (opts.id) body.subnet_id = opts.id;
1303
+ if (opts.description) body.description = opts.description;
1304
+ try {
1305
+ const res = await acnPost("/subnets", body);
1306
+ const lines = [
1307
+ `Subnet created: ${res.subnet_id}`,
1308
+ ` Visibility : ${res.is_public ? "public" : "private"}`,
1309
+ ` Gateway A2A: ${res.gateway_a2a_url}`,
1310
+ ` Gateway WS : ${res.gateway_ws_url}`
1311
+ ];
1312
+ output(res, lines.join("\n"));
1313
+ } catch (err) {
1314
+ handleError(err);
1315
+ }
1316
+ }
1317
+ );
1318
+ cmd.command("delete <subnet_id>").description("Delete a subnet you own").action(async (subnetId) => {
1319
+ const config = loadConfig();
1320
+ if (!config.api_key) {
1321
+ console.error("No API key found. Run `acn join` first.");
1322
+ process.exit(1);
1323
+ }
1324
+ try {
1325
+ const res = await acnDelete(
1326
+ `/subnets/${subnetId}`
1327
+ );
1328
+ output(res, `Subnet ${res.subnet_id} ${res.status}`);
1329
+ } catch (err) {
1330
+ handleError(err);
1331
+ }
1332
+ });
1239
1333
  return cmd;
1240
1334
  }
1241
1335
 
@@ -1338,6 +1432,18 @@ function followCommand() {
1338
1432
  handleError(err);
1339
1433
  }
1340
1434
  });
1435
+ cmd.command("check <target_id>").description("Check whether you are following a specific agent").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (targetId, opts) => {
1436
+ const agentId = opts.agentId ?? requireAgentId5();
1437
+ try {
1438
+ const res = await acnGet(
1439
+ `/agents/${agentId}/follows/${targetId}`
1440
+ );
1441
+ const label = res.following ? "Following" : "Not following";
1442
+ output(res, `${label} ${targetId}`);
1443
+ } catch (err) {
1444
+ handleError(err);
1445
+ }
1446
+ });
1341
1447
  return cmd;
1342
1448
  }
1343
1449
 
@@ -1355,37 +1461,280 @@ function requireAgentId6() {
1355
1461
  }
1356
1462
  return config.agent_id;
1357
1463
  }
1464
+ function parseCsv(value) {
1465
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
1466
+ }
1467
+ async function showWalletInfo(opts) {
1468
+ const agentId = opts.agentId ?? requireAgentId6();
1469
+ try {
1470
+ const res = await acnGet(`/agents/${agentId}/wallets`);
1471
+ const lines = [`Agent : ${res.agent_id}`];
1472
+ if (res.accepts_payment !== void 0)
1473
+ lines.push(`Accepts payment : ${res.accepts_payment}`);
1474
+ if (res.payment_methods?.length)
1475
+ lines.push(`Methods : ${res.payment_methods.join(", ")}`);
1476
+ if (res.wallet_addresses && Object.keys(res.wallet_addresses).length) {
1477
+ lines.push("Wallets :");
1478
+ for (const [chain, addr] of Object.entries(res.wallet_addresses)) {
1479
+ lines.push(` ${chain.padEnd(10)}: ${addr}`);
1480
+ }
1481
+ }
1482
+ if (res.erc8004) {
1483
+ lines.push(`ERC-8004: token_id=${res.erc8004.token_id} chain=${res.erc8004.chain}`);
1484
+ }
1485
+ if (res.token_pricing) {
1486
+ lines.push(`Pricing : ${JSON.stringify(res.token_pricing)}`);
1487
+ }
1488
+ output(res, lines.join("\n"));
1489
+ } catch (err) {
1490
+ handleError(err);
1491
+ }
1492
+ }
1358
1493
  function walletCommand() {
1359
- return new import_commander12.Command("wallet").description("View agent's wallet and payment info").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
1494
+ const cmd = new import_commander12.Command("wallet").description("View and manage agent's wallet & payment info");
1495
+ cmd.command("info", { isDefault: true }).description("Show wallet, payment methods, pricing, and ERC-8004 status").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(showWalletInfo);
1496
+ cmd.command("set-capability").description("Declare which payment methods, networks, and wallets you accept").requiredOption(
1497
+ "--methods <csv>",
1498
+ "Supported payment methods, e.g. usdc,eth,platform_credits"
1499
+ ).requiredOption("--networks <csv>", "Supported networks, e.g. ethereum,base").option(
1500
+ "--wallets <json>",
1501
+ `Wallet addresses by network, JSON, e.g. '{"ethereum":"0x..."}'`
1502
+ ).option("--no-accepts", "Disable accepting payments (default: enabled)").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(
1503
+ async (opts) => {
1504
+ const agentId = opts.agentId ?? requireAgentId6();
1505
+ let walletMap = {};
1506
+ if (opts.wallets) {
1507
+ try {
1508
+ const parsed = JSON.parse(opts.wallets);
1509
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1510
+ throw new Error("--wallets must be a JSON object");
1511
+ }
1512
+ walletMap = parsed;
1513
+ } catch (err) {
1514
+ console.error(
1515
+ `Invalid --wallets JSON: ${err instanceof Error ? err.message : String(err)}`
1516
+ );
1517
+ process.exit(1);
1518
+ }
1519
+ }
1520
+ const body = {
1521
+ supported_methods: parseCsv(opts.methods),
1522
+ supported_networks: parseCsv(opts.networks),
1523
+ wallet_addresses: walletMap,
1524
+ accepts_payment: opts.accepts !== false
1525
+ };
1526
+ try {
1527
+ const res = await acnPost(
1528
+ `/payments/${agentId}/payment-capability`,
1529
+ body
1530
+ );
1531
+ output(res, `Payment capability ${res.status} for ${res.agent_id}`);
1532
+ } catch (err) {
1533
+ handleError(err);
1534
+ }
1535
+ }
1536
+ );
1537
+ cmd.command("set-pricing").description("Set OpenAI-style per-million-token pricing for your agent (USD)").requiredOption("--input <usd>", "USD per 1M input tokens").requiredOption("--output <usd>", "USD per 1M output tokens").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(
1538
+ async (opts) => {
1539
+ const agentId = opts.agentId ?? requireAgentId6();
1540
+ const inputPrice = Number(opts.input);
1541
+ const outputPrice = Number(opts.output);
1542
+ if (!Number.isFinite(inputPrice) || inputPrice < 0) {
1543
+ console.error("--input must be a non-negative number.");
1544
+ process.exit(1);
1545
+ }
1546
+ if (!Number.isFinite(outputPrice) || outputPrice < 0) {
1547
+ console.error("--output must be a non-negative number.");
1548
+ process.exit(1);
1549
+ }
1550
+ const body = {
1551
+ input_price_per_million: inputPrice,
1552
+ output_price_per_million: outputPrice
1553
+ };
1554
+ try {
1555
+ const res = await acnPost(`/payments/${agentId}/token-pricing`, body);
1556
+ const fee = res.network_fee_rate !== void 0 ? `, network fee rate ${res.network_fee_rate}` : "";
1557
+ output(
1558
+ res,
1559
+ `Pricing ${res.status} for ${res.agent_id}: input=$${res.token_pricing.input_price_per_million}/1M, output=$${res.token_pricing.output_price_per_million}/1M (${res.token_pricing.currency})${fee}`
1560
+ );
1561
+ } catch (err) {
1562
+ handleError(err);
1563
+ }
1564
+ }
1565
+ );
1566
+ cmd.command("tasks").description("List the payment tasks the current agent is involved in").option("--status <s>", "Filter by status (e.g. created, payment_confirmed, task_completed)").option("--limit <n>", "Max number of tasks to return (default 50)").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
1567
+ const agentId = opts.agentId ?? requireAgentId6();
1568
+ const params = {};
1569
+ if (opts.status) params.status = opts.status;
1570
+ if (opts.limit !== void 0) {
1571
+ const limit = Number(opts.limit);
1572
+ if (!Number.isFinite(limit) || limit <= 0) {
1573
+ console.error("--limit must be a positive number.");
1574
+ process.exit(1);
1575
+ }
1576
+ params.limit = limit;
1577
+ }
1578
+ try {
1579
+ const res = await acnGet(
1580
+ `/payments/tasks/agent/${agentId}`,
1581
+ params
1582
+ );
1583
+ const tasks = res.tasks ?? [];
1584
+ if (tasks.length === 0) {
1585
+ output(res, `No payment tasks for ${agentId}.`);
1586
+ return;
1587
+ }
1588
+ const lines = [
1589
+ `${tasks.length} task(s) for ${agentId}:`,
1590
+ ` ${"TASK_ID".padEnd(36)} ROLE ${"STATUS".padEnd(20)} AMOUNT VIA COUNTERPARTY`
1591
+ ];
1592
+ for (const t of tasks) {
1593
+ const role = t.buyer_agent === agentId ? "pay " : "recv";
1594
+ const counterparty = t.buyer_agent === agentId ? t.seller_agent : t.buyer_agent;
1595
+ const method = t.payment_method ? `${t.payment_method}` : "?";
1596
+ const network = t.network ? `/${t.network}` : "";
1597
+ lines.push(
1598
+ ` ${t.task_id.padEnd(36)} ${role} ${t.status.padEnd(20)} ${t.amount} ${t.currency ?? ""} via ${method}${network} -> ${counterparty}`
1599
+ );
1600
+ }
1601
+ output(res, lines.join("\n"));
1602
+ } catch (err) {
1603
+ handleError(err);
1604
+ }
1605
+ });
1606
+ cmd.command("stats").description("Show the current agent's payment statistics").option("-i, --agent-id <id>", "Agent ID (defaults to config)").action(async (opts) => {
1360
1607
  const agentId = opts.agentId ?? requireAgentId6();
1361
1608
  try {
1362
- const res = await acnGet(`/agents/${agentId}/wallets`);
1363
- const lines = [`Agent : ${res.agent_id}`];
1364
- if (res.accepts_payment !== void 0)
1365
- lines.push(`Accepts payment : ${res.accepts_payment}`);
1366
- if (res.payment_methods?.length)
1367
- lines.push(`Methods : ${res.payment_methods.join(", ")}`);
1368
- if (res.wallet_addresses && Object.keys(res.wallet_addresses).length) {
1369
- lines.push("Wallets :");
1370
- for (const [chain, addr] of Object.entries(res.wallet_addresses)) {
1371
- lines.push(` ${chain.padEnd(10)}: ${addr}`);
1372
- }
1609
+ const res = await acnGet(`/payments/stats/${agentId}`);
1610
+ const lines = [`Stats for ${agentId}:`];
1611
+ if (res.total_tasks !== void 0)
1612
+ lines.push(` Total tasks : ${res.total_tasks}`);
1613
+ if (res.completed_transactions !== void 0)
1614
+ lines.push(` Completed transactions : ${res.completed_transactions}`);
1615
+ if (res.as_buyer) {
1616
+ lines.push(
1617
+ ` As buyer : ${res.as_buyer.count} task(s), total ${res.as_buyer.total_amount}`
1618
+ );
1373
1619
  }
1374
- if (res.erc8004) {
1375
- lines.push(`ERC-8004: token_id=${res.erc8004.token_id} chain=${res.erc8004.chain}`);
1620
+ if (res.as_seller) {
1621
+ lines.push(
1622
+ ` As seller : ${res.as_seller.count} task(s), total ${res.as_seller.total_amount}`
1623
+ );
1376
1624
  }
1377
- if (res.token_pricing) {
1378
- lines.push(`Pricing : ${JSON.stringify(res.token_pricing)}`);
1625
+ if (res.by_status && Object.keys(res.by_status).length) {
1626
+ lines.push(" By status:");
1627
+ const entries = Object.entries(res.by_status).sort((a, b) => b[1] - a[1]);
1628
+ for (const [status, count] of entries) {
1629
+ lines.push(` ${status.padEnd(20)} ${count}`);
1630
+ }
1379
1631
  }
1380
1632
  output(res, lines.join("\n"));
1381
1633
  } catch (err) {
1382
1634
  handleError(err);
1383
1635
  }
1384
1636
  });
1637
+ cmd.command("estimate <agent_id>").description("Estimate the cost of calling another agent before invoking").requiredOption("--input-tokens <n>", "Estimated input token count").requiredOption("--output-tokens <n>", "Estimated output token count").action(
1638
+ async (agentId, opts) => {
1639
+ const inputTokens = Number(opts.inputTokens);
1640
+ const outputTokens = Number(opts.outputTokens);
1641
+ if (!Number.isFinite(inputTokens) || inputTokens < 0) {
1642
+ console.error("--input-tokens must be a non-negative number.");
1643
+ process.exit(1);
1644
+ }
1645
+ if (!Number.isFinite(outputTokens) || outputTokens < 0) {
1646
+ console.error("--output-tokens must be a non-negative number.");
1647
+ process.exit(1);
1648
+ }
1649
+ try {
1650
+ const res = await acnPost("/payments/billing/estimate", {
1651
+ agent_id: agentId,
1652
+ estimated_input_tokens: inputTokens,
1653
+ estimated_output_tokens: outputTokens
1654
+ });
1655
+ const e = res.estimate ?? {};
1656
+ const lines = [
1657
+ `Estimate for ${res.agent_id} (${inputTokens} in / ${outputTokens} out tokens):`,
1658
+ ` Total : $${e.total_usd ?? "?"} (${e.total_credits ?? "?"} credits)`,
1659
+ ` Network fee : $${e.network_fee_usd ?? "?"} (${e.network_fee_credits ?? "?"} credits)`,
1660
+ ` Agent income : $${e.agent_income_usd ?? "?"} (${e.agent_income_credits ?? "?"} credits)`
1661
+ ];
1662
+ if (res.note) lines.push(` Note: ${res.note}`);
1663
+ output(res, lines.join("\n"));
1664
+ } catch (err) {
1665
+ handleError(err);
1666
+ }
1667
+ }
1668
+ );
1669
+ return cmd;
1670
+ }
1671
+
1672
+ // src/commands/pay.ts
1673
+ var import_commander13 = require("commander");
1674
+ function requireAgentId7() {
1675
+ const config = loadConfig();
1676
+ if (!config.api_key) {
1677
+ console.error("No API key found. Run `acn join` first or `acn config set api-key <key>`.");
1678
+ process.exit(1);
1679
+ }
1680
+ if (!config.agent_id) {
1681
+ console.error("No agent ID found. Run `acn join` first or `acn config set agent-id <id>`.");
1682
+ process.exit(1);
1683
+ }
1684
+ return config.agent_id;
1685
+ }
1686
+ function payCommand() {
1687
+ const cmd = new import_commander13.Command("pay").description("Create a payment task to another agent");
1688
+ cmd.requiredOption("--to <agent>", "Recipient agent ID").requiredOption("--amount <n>", "Payment amount (positive number)").requiredOption("--currency <c>", "Currency code, e.g. USD, USDC").requiredOption("--method <m>", "Payment method, e.g. usdc, eth, platform_credits").requiredOption("--network <n>", "Network, e.g. ethereum, base, solana").option("--description <text>", "Free-text description for the payment task").option("--metadata <json>", "Additional metadata as JSON object").action(
1689
+ async (opts) => {
1690
+ const fromAgent = requireAgentId7();
1691
+ const amount = Number(opts.amount);
1692
+ if (!Number.isFinite(amount) || amount <= 0) {
1693
+ console.error("--amount must be a positive number.");
1694
+ process.exit(1);
1695
+ }
1696
+ let metadata;
1697
+ if (opts.metadata) {
1698
+ try {
1699
+ const parsed = JSON.parse(opts.metadata);
1700
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1701
+ throw new Error("--metadata must be a JSON object");
1702
+ }
1703
+ metadata = parsed;
1704
+ } catch (err) {
1705
+ console.error(
1706
+ `Invalid --metadata JSON: ${err instanceof Error ? err.message : String(err)}`
1707
+ );
1708
+ process.exit(1);
1709
+ }
1710
+ }
1711
+ const body = {
1712
+ from_agent: fromAgent,
1713
+ to_agent: opts.to,
1714
+ amount,
1715
+ currency: opts.currency,
1716
+ payment_method: opts.method,
1717
+ network: opts.network
1718
+ };
1719
+ if (opts.description !== void 0) body.description = opts.description;
1720
+ if (metadata !== void 0) body.metadata = metadata;
1721
+ try {
1722
+ const res = await acnPost("/payments/tasks", body);
1723
+ output(
1724
+ res,
1725
+ `Payment task ${res.status}: ${res.task_id}
1726
+ ${fromAgent} -> ${opts.to}: ${amount} ${opts.currency} via ${opts.method}/${opts.network}`
1727
+ );
1728
+ } catch (err) {
1729
+ handleError(err);
1730
+ }
1731
+ }
1732
+ );
1733
+ return cmd;
1385
1734
  }
1386
1735
 
1387
1736
  // src/index.ts
1388
- var program = new import_commander13.Command();
1737
+ var program = new import_commander14.Command();
1389
1738
  program.name("acn").description("ACN CLI \u2014 Agent Collaboration Network command-line interface").version("0.1.0").option("--json", "Output raw JSON (useful for agent parsing)").hook("preAction", (thisCommand) => {
1390
1739
  const opts = thisCommand.opts();
1391
1740
  if (opts.json) setJsonMode(true);
@@ -1402,4 +1751,5 @@ program.addCommand(sessionCommand());
1402
1751
  program.addCommand(subnetCommand());
1403
1752
  program.addCommand(followCommand());
1404
1753
  program.addCommand(walletCommand());
1754
+ program.addCommand(payCommand());
1405
1755
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acnlabs/acn-cli",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Official CLI for ACN (Agent Collaboration Network) — zero-integration agent access",
5
5
  "main": "dist/index.js",
6
6
  "bin": {