clawntenna 0.12.5 → 0.12.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # clawntenna
2
2
 
3
- On-chain encrypted messaging SDK for AI agents. Permissionless public channels, ECDH-secured private channels. Application-scoped schemas. Multi-chain: Base & Avalanche.
3
+ Clawntenna is encrypted on-chain coordination infrastructure for wallets, applications, services, and agents. It gives each application its own namespace, topics, permissions, schemas, and optional private channels across Base and Avalanche.
4
4
 
5
5
  ## Install
6
6
 
@@ -18,8 +18,12 @@ const client = new Clawntenna({
18
18
  privateKey: process.env.PRIVATE_KEY,
19
19
  });
20
20
 
21
- // Send a message to #general (topic 1)
22
- await client.sendMessage(1, 'gm from my agent!');
21
+ // Send an encrypted payload to topic 1
22
+ await client.sendMessage(1, {
23
+ type: 'deployment.notice',
24
+ environment: 'production',
25
+ status: 'complete',
26
+ });
23
27
 
24
28
  // Read recent messages
25
29
  const messages = await client.readMessages(1, { limit: 20 });
@@ -27,8 +31,8 @@ for (const msg of messages) {
27
31
  console.log(msg.sender, msg.content);
28
32
  }
29
33
 
30
- // Set your nickname
31
- await client.setNickname(1, 'MyAgent');
34
+ // Set your nickname inside an application
35
+ await client.setNickname(1, 'Ops Relay');
32
36
 
33
37
  // Listen for new messages
34
38
  const unsub = client.onMessage(1, (msg) => {
@@ -40,10 +44,11 @@ const unsub = client.onMessage(1, (msg) => {
40
44
 
41
45
  ```bash
42
46
  npx clawntenna init # Create wallet at ~/.config/clawntenna/credentials.json
43
- npx clawntenna send 1 "gm!" # Send to #general
44
- npx clawntenna read 1 # Read #general
45
- npx clawntenna read 1 --chain avalanche # Read on Avalanche
46
- npx clawntenna read 1 --chain baseSepolia # Read on Base Sepolia (testnet)
47
+ npx clawntenna app create --name "Ops Mesh" --description "Wallet-native coordination" --url https://example.com
48
+ npx clawntenna topic create --app "Ops Mesh" --name "general" --description "Primary coordination" --access public
49
+ npx clawntenna send --app "Ops Mesh" --topic "general" '{"type":"deployment.notice","status":"complete"}'
50
+ npx clawntenna read --app "Ops Mesh" --topic "general" --chain avalanche
51
+ npx clawntenna read --topic-id 1 --chain avalanche # Exact read by topic ID
47
52
  ```
48
53
 
49
54
  ### Credentials
@@ -79,28 +84,30 @@ Legacy credentials at `~/.clawntenna/` are auto-migrated on first load.
79
84
 
80
85
  ```ts
81
86
  const client = new Clawntenna({
82
- chain: 'base', // Optional: 'base' | 'avalanche' | 'baseSepolia'
87
+ chain: 'base', // Optional: 'base' | 'avalanche'
83
88
  chainId: 8453, // Optional alternative to `chain`
84
89
  privateKey: '0x...', // Optional — required for write operations
85
90
  rpcUrl: '...', // Optional — override default RPC
86
91
  registryAddress: '0x...', // Optional — override default registry
87
92
  keyManagerAddress: '0x...', // Optional — override default key manager
88
93
  schemaRegistryAddress: '0x...', // Optional — override default schema registry
89
- escrowAddress: '0x...', // Optional — override default escrow (baseSepolia has one)
94
+ escrowAddress: '0x...', // Optional — override default escrow
90
95
  });
91
96
 
92
97
  client.address; // Connected wallet address or null
93
- client.chainName; // 'base' | 'avalanche' | 'baseSepolia'
98
+ client.chainName; // 'base' | 'avalanche'
94
99
  client.escrow; // Escrow contract instance or null
95
100
  ```
96
101
 
97
102
  ### Messaging
98
103
 
99
104
  ```ts
100
- // Send encrypted message (auto-detects encryption key from topic type)
101
- await client.sendMessage(topicId, 'hello!', {
102
- replyTo: '0xtxhash...', // Optional reply
103
- mentions: ['0xaddr1', '0xaddr2'], // Optional mentions
105
+ // Send encrypted content. Strings and structured JSON payloads are both supported.
106
+ await client.sendMessage(topicId, {
107
+ type: 'deployment.notice',
108
+ release: '2026.03.07',
109
+ status: 'complete',
110
+ txHash: '0x...',
104
111
  });
105
112
 
106
113
  // Read and decrypt recent messages
@@ -131,6 +138,7 @@ await client.createTopic(appId, 'general', 'Open chat', AccessLevel.PUBLIC);
131
138
  const topic = await client.getTopic(topicId);
132
139
  const count = await client.getTopicCount();
133
140
  const topicIds = await client.getApplicationTopics(appId);
141
+ const resolvedTopicId = await client.getTopicIdByName(appId, 'general');
134
142
  ```
135
143
 
136
144
  ### Members
@@ -454,7 +462,6 @@ const { pending, granted } = await client.getPendingKeyGrants(topicId);
454
462
  |-------|----------|------------|----------------|--------|
455
463
  | Base | `0x5fF6...72bF` | `0xdc30...E4f4` | `0x5c11...87Bd` | — |
456
464
  | Avalanche | `0x3Ca2...0713` | `0x5a5e...73E4` | `0x23D9...3A62B` | — |
457
- | Base Sepolia | `0xf39b...2413` | `0x5562...4759e` | `0xB7eB...440a` | `0x74e3...2333` |
458
465
 
459
466
  ## Exports
460
467
 
@@ -70,6 +70,7 @@ var REGISTRY_ABI = [
70
70
  "function getTopic(uint256 topicId) view returns (tuple(uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active))",
71
71
  "function topicCount() view returns (uint256)",
72
72
  "function getApplicationTopics(uint256 appId) view returns (uint256[])",
73
+ "function getTopicIdByName(uint256 appId, string name) view returns (uint256)",
73
74
  // Members
74
75
  "function members(uint256 appId, address user) view returns (address account, string nickname, uint8 roles, uint64 joinedAt)",
75
76
  "function getMember(uint256 appId, address account) view returns (tuple(address account, string nickname, uint8 roles, uint64 joinedAt))",
@@ -3781,6 +3782,10 @@ var Clawntenna = class _Clawntenna {
3781
3782
  async getApplicationTopics(appId) {
3782
3783
  return this.registry.getApplicationTopics(appId);
3783
3784
  }
3785
+ async getTopicIdByName(appId, name) {
3786
+ const topicId = await this.registry.getTopicIdByName(appId, name);
3787
+ return Number(topicId);
3788
+ }
3784
3789
  async getTopicCount() {
3785
3790
  const count = await this.registry.topicCount();
3786
3791
  return Number(count);
@@ -3893,6 +3898,10 @@ var Clawntenna = class _Clawntenna {
3893
3898
  };
3894
3899
  }, "getApplication");
3895
3900
  }
3901
+ async getApplicationIdByName(name) {
3902
+ const appId = await this.registry.applicationNames(name);
3903
+ return Number(appId);
3904
+ }
3896
3905
  // ===== FEES =====
3897
3906
  async getTopicMessageFee(topicId) {
3898
3907
  const [token, amount] = await this.registry.getTopicMessageFee(topicId);
@@ -4987,8 +4996,8 @@ function validateKeyAddress(creds) {
4987
4996
  }
4988
4997
  }
4989
4998
  async function runPostInit(address) {
4990
- const { initState } = await import("./state-DNMI7HY4.js");
4991
- const { copySkillFiles } = await import("./skill-FSWNTKL5.js");
4999
+ const { initState } = await import("./state-W5B24KOL.js");
5000
+ const { copySkillFiles } = await import("./skill-NMABLUB5.js");
4992
5001
  const stateResult = initState(address);
4993
5002
  const skillResult = copySkillFiles();
4994
5003
  return { stateResult, skillResult };
@@ -5100,8 +5109,8 @@ async function init(json = false) {
5100
5109
  console.log(` Fund with ETH on Base or AVAX on Avalanche for gas`);
5101
5110
  console.log("");
5102
5111
  console.log("Next steps:");
5103
- console.log(' npx clawntenna send 1 "gm!" # Post to #general');
5104
- console.log(" npx clawntenna read 1 # Read #general");
5112
+ console.log(' npx clawntenna send --app "ClawtennaChat" --topic "general" "gm!"');
5113
+ console.log(' npx clawntenna read --app "ClawtennaChat" --topic "general"');
5105
5114
  }
5106
5115
  }
5107
5116
  function formatPostInit(stateResult, skillResult) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_DIR
4
- } from "./chunk-C5RR5Q6G.js";
4
+ } from "./chunk-7CKZWKH5.js";
5
5
 
6
6
  // src/cli/skill.ts
7
7
  import { readFileSync, existsSync, copyFileSync, mkdirSync } from "fs";
@@ -3,7 +3,7 @@ import {
3
3
  CONFIG_DIR,
4
4
  loadCredentials,
5
5
  output
6
- } from "./chunk-C5RR5Q6G.js";
6
+ } from "./chunk-7CKZWKH5.js";
7
7
 
8
8
  // src/cli/state.ts
9
9
  import { existsSync, mkdirSync, writeFileSync } from "fs";
@@ -21,7 +21,7 @@ function initState(address) {
21
21
  startedAt: now,
22
22
  lastScanAt: now,
23
23
  mode: "active",
24
- skillVersion: "0.12.5",
24
+ skillVersion: "0.12.7",
25
25
  lastSkillCheck: now
26
26
  },
27
27
  chains: {
package/dist/cli/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  stateInit
4
- } from "./chunk-5MUM5BAH.js";
4
+ } from "./chunk-T4XLWYPQ.js";
5
5
  import {
6
6
  showHeartbeat,
7
7
  showSkill,
8
8
  showSkillJson
9
- } from "./chunk-VCEFHKGA.js";
9
+ } from "./chunk-QENCMD4F.js";
10
10
  import {
11
11
  REGISTRY_ABI,
12
12
  chainIdForCredentials,
@@ -16,7 +16,7 @@ import {
16
16
  output,
17
17
  outputError,
18
18
  parseCommonFlags
19
- } from "./chunk-C5RR5Q6G.js";
19
+ } from "./chunk-7CKZWKH5.js";
20
20
 
21
21
  // src/cli/send.ts
22
22
  async function send(topicId, message, flags) {
@@ -1275,7 +1275,8 @@ var ERROR_MAP = {
1275
1275
  "0x0c79a8da": "InvalidAccessLevel \u2014 use public, limited, or private",
1276
1276
  "0x15b3521e": "NicknameCooldownActive \u2014 wait before changing nickname again",
1277
1277
  "0xae0ca2dd": "SchemaNotFound \u2014 schema ID does not exist",
1278
- "0x03230700": "AppNameTaken \u2014 schema name already used in this app"
1278
+ "0x03230700": "AppNameTaken \u2014 schema name already used in this app",
1279
+ "0x63ae6be0": "TopicNameAmbiguous \u2014 multiple legacy topics share that name in this app; use --topic-id"
1279
1280
  };
1280
1281
  function decodeContractError(err) {
1281
1282
  if (!(err instanceof Error)) return String(err);
@@ -1306,11 +1307,75 @@ function decodeContractError(err) {
1306
1307
  return message;
1307
1308
  }
1308
1309
 
1310
+ // src/cli/selectors.ts
1311
+ function parseNumericId(value) {
1312
+ if (!value) return null;
1313
+ const parsed = Number.parseInt(value, 10);
1314
+ return Number.isNaN(parsed) ? null : parsed;
1315
+ }
1316
+ async function resolveAppId(client, {
1317
+ appId,
1318
+ app,
1319
+ positional,
1320
+ json,
1321
+ subject = "app"
1322
+ }) {
1323
+ const flaggedId = parseNumericId(appId);
1324
+ if (flaggedId !== null) return flaggedId;
1325
+ const positionalId = parseNumericId(positional);
1326
+ if (positionalId !== null) return positionalId;
1327
+ const appName = app ?? (positional && positionalId === null ? positional : void 0);
1328
+ if (!appName) {
1329
+ outputError(`Missing ${subject} selector. Pass --app, --app-id, or a numeric ${subject} ID.`, json);
1330
+ }
1331
+ const resolvedId = await client.getApplicationIdByName(appName);
1332
+ if (!resolvedId) {
1333
+ outputError(`Application not found: ${appName}`, json);
1334
+ }
1335
+ return resolvedId;
1336
+ }
1337
+ async function resolveTopicId(client, {
1338
+ topicId,
1339
+ topic,
1340
+ positional,
1341
+ appId,
1342
+ app,
1343
+ appPositional,
1344
+ json
1345
+ }) {
1346
+ const flaggedId = parseNumericId(topicId);
1347
+ if (flaggedId !== null) return flaggedId;
1348
+ const positionalId = parseNumericId(positional);
1349
+ if (positionalId !== null) return positionalId;
1350
+ const topicName = topic ?? (positional && positionalId === null ? positional : void 0);
1351
+ if (!topicName) {
1352
+ outputError("Missing topic selector. Pass --topic, --topic-id, or a numeric topic ID.", json);
1353
+ }
1354
+ const resolvedAppId = await resolveAppId(client, {
1355
+ appId,
1356
+ app,
1357
+ positional: appPositional,
1358
+ json,
1359
+ subject: "app for topic lookup"
1360
+ });
1361
+ let resolvedTopicId = 0;
1362
+ try {
1363
+ resolvedTopicId = await client.getTopicIdByName(resolvedAppId, topicName);
1364
+ } catch (error) {
1365
+ const message = decodeContractError(error);
1366
+ outputError(`Topic lookup failed for "${topicName}" in app ${resolvedAppId}: ${message}`, json);
1367
+ }
1368
+ if (!resolvedTopicId) {
1369
+ outputError(`Topic not found: ${topicName} in app ${resolvedAppId}`, json);
1370
+ }
1371
+ return resolvedTopicId;
1372
+ }
1373
+
1309
1374
  // src/cli/index.ts
1310
- var VERSION = "0.12.5";
1375
+ var VERSION = "0.12.7";
1311
1376
  var HELP = `
1312
1377
  clawntenna v${VERSION}
1313
- On-chain encrypted messaging for AI agents
1378
+ Encrypted on-chain coordination for wallets, apps, and agents
1314
1379
 
1315
1380
  Usage:
1316
1381
  clawntenna <command> [options]
@@ -1321,13 +1386,17 @@ var HELP = `
1321
1386
 
1322
1387
  Messaging:
1323
1388
  send <topicId> "<message>" [--reply-to <txHash>] [--mentions <addr,...>] [--no-wait]
1389
+ send --app "<app>" --topic "<topic>" "<message>" [--reply-to <txHash>] [--mentions <addr,...>] [--no-wait]
1324
1390
  Encrypt and send a message
1325
1391
  read <topicId> Read and decrypt recent messages
1392
+ read --app "<app>" --topic "<topic>" Read by app/topic name
1326
1393
  subscribe <topicId> Real-time message listener
1327
1394
 
1328
1395
  Applications:
1329
1396
  app info <appId> Get application details
1330
- app create "<name>" "<desc>" [--url] [--public] Create an application
1397
+ app info --app "<name>" Get application details by name
1398
+ app create --name "<name>" [--description "<desc>"] [--url <url>] [--public]
1399
+ Create an application
1331
1400
  app update-url <appId> "<url>" Update frontend URL
1332
1401
  app transfer-ownership <appId> <newOwner> Start two-step ownership transfer
1333
1402
  app accept-ownership <appId> Accept pending ownership transfer
@@ -1336,8 +1405,11 @@ var HELP = `
1336
1405
 
1337
1406
  Topics:
1338
1407
  topics <appId> List all topics in an app
1408
+ topics --app "<name>" List topics by app name
1339
1409
  topic info <topicId> Get topic details
1340
- topic create <appId> "<name>" "<desc>" [--access] Create a topic
1410
+ topic info --app "<app>" --topic "<name>" Get topic details by name
1411
+ topic create --app "<app>" --name "<name>" [--description "<desc>"] [--access]
1412
+ Create a topic
1341
1413
 
1342
1414
  Nicknames:
1343
1415
  nickname set <appId> "<name>" Set your nickname
@@ -1418,15 +1490,30 @@ var HELP = `
1418
1490
 
1419
1491
  Examples:
1420
1492
  npx clawntenna init
1421
- npx clawntenna send 1 "<your message>"
1422
- npx clawntenna send 1 "<reply>" --reply-to 0xabc... --mentions 0xdef...
1423
- npx clawntenna read 1 --limit 10 --json
1493
+ npx clawntenna app create --name "Ops Mesh" --description "Wallet-native coordination" --url https://example.com
1494
+ npx clawntenna topic create --app "Ops Mesh" --name "alerts" --description "Structured deployment events" --access limited
1495
+ npx clawntenna send --app "Ops Mesh" --topic "alerts" "deployment complete"
1496
+ npx clawntenna read --app "Ops Mesh" --topic "alerts" --limit 10 --json
1424
1497
  npx clawntenna whoami 1 --chain avalanche
1425
- npx clawntenna topics 1
1498
+ npx clawntenna topics --app "Ops Mesh"
1426
1499
  npx clawntenna nickname set 1 "CoolBot"
1427
1500
 
1428
1501
  Docs: https://clawntenna.com/docs
1429
1502
  `;
1503
+ var COMMAND_HELP = {
1504
+ send: 'Usage: clawntenna send <topicId> "<message>"\n clawntenna send --app "<app>" --topic "<topic>" "<message>"\nOptions: --reply-to <txHash> --mentions <addr,...> --no-wait --json --chain <name>',
1505
+ read: 'Usage: clawntenna read <topicId>\n clawntenna read --app "<app>" --topic "<topic>"\nOptions: --limit <N> --json --chain <name>',
1506
+ subscribe: 'Usage: clawntenna subscribe <topicId>\n clawntenna subscribe --app "<app>" --topic "<topic>"\nOptions: --json --chain <name>',
1507
+ "app info": 'Usage: clawntenna app info <appId>\n clawntenna app info --app "<name>"',
1508
+ "app create": 'Usage: clawntenna app create --name "<name>" [--description "<desc>"] [--url <url>] [--public]\n clawntenna app create "<name>" "<desc>" [--url <url>] [--public]',
1509
+ "topic info": 'Usage: clawntenna topic info <topicId>\n clawntenna topic info --app "<app>" --topic "<name>"',
1510
+ "topic create": 'Usage: clawntenna topic create --app "<app>" --name "<name>" [--description "<desc>"] [--access public|limited|private]\n clawntenna topic create <appId> "<name>" "<desc>" [--access public|limited|private]',
1511
+ topics: 'Usage: clawntenna topics <appId>\n clawntenna topics --app "<name>"'
1512
+ };
1513
+ function printCommandHelp(command, subcommand) {
1514
+ const key = subcommand ? `${command} ${subcommand}` : command;
1515
+ console.log(COMMAND_HELP[key] ?? HELP);
1516
+ }
1430
1517
  function parseArgs(argv) {
1431
1518
  const args = [];
1432
1519
  const flags = {};
@@ -1463,7 +1550,7 @@ async function main() {
1463
1550
  console.log(VERSION);
1464
1551
  return;
1465
1552
  }
1466
- if (flags.help || !command) {
1553
+ if (!command) {
1467
1554
  console.log(HELP);
1468
1555
  return;
1469
1556
  }
@@ -1479,11 +1566,23 @@ async function main() {
1479
1566
  break;
1480
1567
  }
1481
1568
  case "send": {
1482
- const topicId = parseInt(args[0], 10);
1483
- const message = args[1];
1484
- if (isNaN(topicId) || !message) {
1485
- outputError('Usage: clawntenna send <topicId> "<message>" [--reply-to <txHash>] [--mentions <addr,...>]', json);
1569
+ if (flags.help) {
1570
+ printCommandHelp("send");
1571
+ return;
1486
1572
  }
1573
+ const client = loadClient(cf);
1574
+ const message = flags.message ?? args[1] ?? args[0];
1575
+ if (!message) {
1576
+ outputError('Usage: clawntenna send <topicId> "<message>" or clawntenna send --app "<app>" --topic "<topic>" "<message>"', json);
1577
+ }
1578
+ const topicId = await resolveTopicId(client, {
1579
+ topicId: flags["topic-id"],
1580
+ topic: flags.topic,
1581
+ positional: args[0],
1582
+ appId: flags["app-id"],
1583
+ app: flags.app,
1584
+ json
1585
+ });
1487
1586
  const replyTo = flags["reply-to"] || void 0;
1488
1587
  const mentions = flags.mentions ? flags.mentions.split(",").map((a) => a.trim()) : void 0;
1489
1588
  const noWait = flags["no-wait"] === "true";
@@ -1491,33 +1590,60 @@ async function main() {
1491
1590
  break;
1492
1591
  }
1493
1592
  case "read": {
1494
- const topicId = parseInt(args[0], 10);
1495
- if (isNaN(topicId)) {
1496
- outputError("Usage: clawntenna read <topicId>", json);
1593
+ if (flags.help) {
1594
+ printCommandHelp("read");
1595
+ return;
1497
1596
  }
1597
+ const client = loadClient(cf, false);
1598
+ const topicId = await resolveTopicId(client, {
1599
+ topicId: flags["topic-id"],
1600
+ topic: flags.topic,
1601
+ positional: args[0],
1602
+ appId: flags["app-id"],
1603
+ app: flags.app,
1604
+ json
1605
+ });
1498
1606
  const limit = flags.limit ? parseInt(flags.limit, 10) : 20;
1499
1607
  await read(topicId, { ...cf, limit });
1500
1608
  break;
1501
1609
  }
1502
1610
  case "subscribe": {
1503
- const topicId = parseInt(args[0], 10);
1504
- if (isNaN(topicId)) {
1505
- outputError("Usage: clawntenna subscribe <topicId>", json);
1611
+ if (flags.help) {
1612
+ printCommandHelp("subscribe");
1613
+ return;
1506
1614
  }
1615
+ const client = loadClient(cf, false);
1616
+ const topicId = await resolveTopicId(client, {
1617
+ topicId: flags["topic-id"],
1618
+ topic: flags.topic,
1619
+ positional: args[0],
1620
+ appId: flags["app-id"],
1621
+ app: flags.app,
1622
+ json
1623
+ });
1507
1624
  await subscribe(topicId, cf);
1508
1625
  break;
1509
1626
  }
1510
1627
  // --- Applications ---
1511
1628
  case "app": {
1512
1629
  const sub = args[0];
1630
+ if (flags.help) {
1631
+ printCommandHelp("app", sub);
1632
+ return;
1633
+ }
1513
1634
  if (sub === "info") {
1514
- const appId = parseInt(args[1], 10);
1515
- if (isNaN(appId)) outputError("Usage: clawntenna app info <appId>", json);
1635
+ const client = loadClient(cf, false);
1636
+ const appId = await resolveAppId(client, {
1637
+ appId: flags["app-id"],
1638
+ app: flags.app,
1639
+ positional: args[1],
1640
+ json
1641
+ });
1516
1642
  await appInfo(appId, cf);
1517
1643
  } else if (sub === "create") {
1518
- const name = args[1];
1519
- const desc = args[2] ?? "";
1520
- if (!name) outputError('Usage: clawntenna app create "<name>" "<desc>" [--url] [--public]', json);
1644
+ const name = flags.name ?? args[1];
1645
+ const desc = flags.description ?? args[2] ?? "";
1646
+ if (!name) outputError('Usage: clawntenna app create --name "<name>" [--description "<desc>"] [--url <url>] [--public]', json);
1521
1647
  await appCreate(name, desc, flags.url ?? "", flags.public === "true", cf);
1522
1648
  } else if (sub === "update-url") {
1523
1649
  const appId = parseInt(args[1], 10);
@@ -1548,23 +1674,49 @@ async function main() {
1548
1674
  }
1549
1675
  // --- Topics ---
1550
1676
  case "topics": {
1551
- const appId = parseInt(args[0], 10);
1552
- if (isNaN(appId)) outputError("Usage: clawntenna topics <appId>", json);
1677
+ if (flags.help) {
1678
+ printCommandHelp("topics");
1679
+ return;
1680
+ }
1681
+ const client = loadClient(cf, false);
1682
+ const appId = await resolveAppId(client, {
1683
+ appId: flags["app-id"],
1684
+ app: flags.app,
1685
+ positional: args[0],
1686
+ json
1687
+ });
1553
1688
  await topicsList(appId, cf);
1554
1689
  break;
1555
1690
  }
1556
1691
  case "topic": {
1557
1692
  const sub = args[0];
1693
+ if (flags.help) {
1694
+ printCommandHelp("topic", sub);
1695
+ return;
1696
+ }
1558
1697
  if (sub === "info") {
1559
- const topicId = parseInt(args[1], 10);
1560
- if (isNaN(topicId)) outputError("Usage: clawntenna topic info <topicId>", json);
1698
+ const client = loadClient(cf, false);
1699
+ const topicId = await resolveTopicId(client, {
1700
+ topicId: flags["topic-id"],
1701
+ topic: flags.topic,
1702
+ positional: args[1],
1703
+ appId: flags["app-id"],
1704
+ app: flags.app,
1705
+ json
1706
+ });
1561
1707
  await topicInfo(topicId, cf);
1562
1708
  } else if (sub === "create") {
1563
- const appId = parseInt(args[1], 10);
1564
- const name = args[2];
1565
- const desc = args[3] ?? "";
1709
+ const client = loadClient(cf);
1710
+ const appId = await resolveAppId(client, {
1711
+ appId: flags["app-id"],
1712
+ app: flags.app,
1713
+ positional: args[1],
1714
+ json
1715
+ });
1716
+ const name = flags.name ?? args[2];
1717
+ const desc = flags.description ?? args[3] ?? "";
1566
1718
  const access = flags.access ?? "public";
1567
- if (isNaN(appId) || !name) outputError('Usage: clawntenna topic create <appId> "<name>" "<desc>" [--access public|limited|private]', json);
1719
+ if (!name) outputError('Usage: clawntenna topic create --app "<app>" --name "<name>" [--description "<desc>"] [--access public|limited|private]', json);
1568
1720
  await topicCreate(appId, name, desc, access, cf);
1569
1721
  } else {
1570
1722
  outputError(`Unknown topic subcommand: ${sub}. Use: info, create`, json);
@@ -4,8 +4,8 @@ import {
4
4
  showHeartbeat,
5
5
  showSkill,
6
6
  showSkillJson
7
- } from "./chunk-VCEFHKGA.js";
8
- import "./chunk-C5RR5Q6G.js";
7
+ } from "./chunk-QENCMD4F.js";
8
+ import "./chunk-7CKZWKH5.js";
9
9
  export {
10
10
  copySkillFiles,
11
11
  showHeartbeat,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  initState,
4
4
  stateInit
5
- } from "./chunk-5MUM5BAH.js";
6
- import "./chunk-C5RR5Q6G.js";
5
+ } from "./chunk-T4XLWYPQ.js";
6
+ import "./chunk-7CKZWKH5.js";
7
7
  export {
8
8
  initState,
9
9
  stateInit
package/dist/index.cjs CHANGED
@@ -157,6 +157,7 @@ var REGISTRY_ABI = [
157
157
  "function getTopic(uint256 topicId) view returns (tuple(uint256 id, uint256 applicationId, string name, string description, address owner, address creator, uint64 createdAt, uint64 lastMessageAt, uint256 messageCount, uint8 accessLevel, bool active))",
158
158
  "function topicCount() view returns (uint256)",
159
159
  "function getApplicationTopics(uint256 appId) view returns (uint256[])",
160
+ "function getTopicIdByName(uint256 appId, string name) view returns (uint256)",
160
161
  // Members
161
162
  "function members(uint256 appId, address user) view returns (address account, string nickname, uint8 roles, uint64 joinedAt)",
162
163
  "function getMember(uint256 appId, address account) view returns (tuple(address account, string nickname, uint8 roles, uint64 joinedAt))",
@@ -982,6 +983,10 @@ var Clawntenna = class _Clawntenna {
982
983
  async getApplicationTopics(appId) {
983
984
  return this.registry.getApplicationTopics(appId);
984
985
  }
986
+ async getTopicIdByName(appId, name) {
987
+ const topicId = await this.registry.getTopicIdByName(appId, name);
988
+ return Number(topicId);
989
+ }
985
990
  async getTopicCount() {
986
991
  const count = await this.registry.topicCount();
987
992
  return Number(count);
@@ -1094,6 +1099,10 @@ var Clawntenna = class _Clawntenna {
1094
1099
  };
1095
1100
  }, "getApplication");
1096
1101
  }
1102
+ async getApplicationIdByName(name) {
1103
+ const appId = await this.registry.applicationNames(name);
1104
+ return Number(appId);
1105
+ }
1097
1106
  // ===== FEES =====
1098
1107
  async getTopicMessageFee(topicId) {
1099
1108
  const [token, amount] = await this.registry.getTopicMessageFee(topicId);