clawntenna 0.12.6 → 0.12.8

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) => {
@@ -42,9 +46,9 @@ const unsub = client.onMessage(1, (msg) => {
42
46
  npx clawntenna init # Create wallet at ~/.config/clawntenna/credentials.json
43
47
  npx clawntenna app create --name "Ops Mesh" --description "Wallet-native coordination" --url https://example.com
44
48
  npx clawntenna topic create --app "Ops Mesh" --name "general" --description "Primary coordination" --access public
45
- npx clawntenna send --app "Ops Mesh" --topic "general" "gm!"
49
+ npx clawntenna send --app "Ops Mesh" --topic "general" '{"type":"deployment.notice","status":"complete"}'
46
50
  npx clawntenna read --app "Ops Mesh" --topic "general" --chain avalanche
47
- npx clawntenna read --topic-id 1 --chain baseSepolia # Exact read on Base Sepolia (testnet)
51
+ npx clawntenna read --topic-id 1 --chain avalanche # Exact read by topic ID
48
52
  ```
49
53
 
50
54
  ### Credentials
@@ -80,28 +84,30 @@ Legacy credentials at `~/.clawntenna/` are auto-migrated on first load.
80
84
 
81
85
  ```ts
82
86
  const client = new Clawntenna({
83
- chain: 'base', // Optional: 'base' | 'avalanche' | 'baseSepolia'
87
+ chain: 'base', // Optional: 'base' | 'avalanche'
84
88
  chainId: 8453, // Optional alternative to `chain`
85
89
  privateKey: '0x...', // Optional — required for write operations
86
90
  rpcUrl: '...', // Optional — override default RPC
87
91
  registryAddress: '0x...', // Optional — override default registry
88
92
  keyManagerAddress: '0x...', // Optional — override default key manager
89
93
  schemaRegistryAddress: '0x...', // Optional — override default schema registry
90
- escrowAddress: '0x...', // Optional — override default escrow (baseSepolia has one)
94
+ escrowAddress: '0x...', // Optional — override default escrow
91
95
  });
92
96
 
93
97
  client.address; // Connected wallet address or null
94
- client.chainName; // 'base' | 'avalanche' | 'baseSepolia'
98
+ client.chainName; // 'base' | 'avalanche'
95
99
  client.escrow; // Escrow contract instance or null
96
100
  ```
97
101
 
98
102
  ### Messaging
99
103
 
100
104
  ```ts
101
- // Send encrypted message (auto-detects encryption key from topic type)
102
- await client.sendMessage(topicId, 'hello!', {
103
- replyTo: '0xtxhash...', // Optional reply
104
- 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...',
105
111
  });
106
112
 
107
113
  // Read and decrypt recent messages
@@ -132,6 +138,7 @@ await client.createTopic(appId, 'general', 'Open chat', AccessLevel.PUBLIC);
132
138
  const topic = await client.getTopic(topicId);
133
139
  const count = await client.getTopicCount();
134
140
  const topicIds = await client.getApplicationTopics(appId);
141
+ const resolvedTopicId = await client.getTopicIdByName(appId, 'general');
135
142
  ```
136
143
 
137
144
  ### Members
@@ -455,7 +462,6 @@ const { pending, granted } = await client.getPendingKeyGrants(topicId);
455
462
  |-------|----------|------------|----------------|--------|
456
463
  | Base | `0x5fF6...72bF` | `0xdc30...E4f4` | `0x5c11...87Bd` | — |
457
464
  | Avalanche | `0x3Ca2...0713` | `0x5a5e...73E4` | `0x23D9...3A62B` | — |
458
- | Base Sepolia | `0xf39b...2413` | `0x5562...4759e` | `0xB7eB...440a` | `0x74e3...2333` |
459
465
 
460
466
  ## Exports
461
467
 
@@ -3,7 +3,7 @@ import {
3
3
  CONFIG_DIR,
4
4
  loadCredentials,
5
5
  output
6
- } from "./chunk-YYE3F3KA.js";
6
+ } from "./chunk-AQHFWEZF.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.6",
24
+ skillVersion: "0.12.8",
25
25
  lastSkillCheck: now
26
26
  },
27
27
  chains: {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_DIR
4
- } from "./chunk-YYE3F3KA.js";
4
+ } from "./chunk-AQHFWEZF.js";
5
5
 
6
6
  // src/cli/skill.ts
7
7
  import { readFileSync, existsSync, copyFileSync, mkdirSync } from "fs";
@@ -4446,6 +4446,16 @@ var Clawntenna = class _Clawntenna {
4446
4446
  this.ecdhPrivateKey = privateKey;
4447
4447
  this.ecdhPublicKey = publicKey;
4448
4448
  }
4449
+ /**
4450
+ * Export loaded ECDH keypair as hex strings for credential persistence.
4451
+ */
4452
+ getECDHKeypairHex() {
4453
+ if (!this.ecdhPrivateKey || !this.ecdhPublicKey) return null;
4454
+ return {
4455
+ privateKey: ethers.hexlify(this.ecdhPrivateKey),
4456
+ publicKey: ethers.hexlify(this.ecdhPublicKey)
4457
+ };
4458
+ }
4449
4459
  /**
4450
4460
  * Register ECDH public key on-chain.
4451
4461
  */
@@ -4996,8 +5006,8 @@ function validateKeyAddress(creds) {
4996
5006
  }
4997
5007
  }
4998
5008
  async function runPostInit(address) {
4999
- const { initState } = await import("./state-4PIASWQ4.js");
5000
- const { copySkillFiles } = await import("./skill-Y3WBBCRF.js");
5009
+ const { initState } = await import("./state-73B57RGU.js");
5010
+ const { copySkillFiles } = await import("./skill-HV2NNR3H.js");
5001
5011
  const stateResult = initState(address);
5002
5012
  const skillResult = copySkillFiles();
5003
5013
  return { stateResult, skillResult };
@@ -5139,6 +5149,10 @@ function loadCredentials() {
5139
5149
  }
5140
5150
  return null;
5141
5151
  }
5152
+ function saveCredentials(credentials) {
5153
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
5154
+ writeFileSync(CREDS_PATH, JSON.stringify(credentials, null, 2), { mode: 384 });
5155
+ }
5142
5156
 
5143
5157
  // src/cli/util.ts
5144
5158
  function parseCommonFlags(flags) {
@@ -5204,7 +5218,8 @@ export {
5204
5218
  chainIdForCredentials,
5205
5219
  CONFIG_DIR,
5206
5220
  init,
5207
- loadCredentials
5221
+ loadCredentials,
5222
+ saveCredentials
5208
5223
  };
5209
5224
  /*! Bundled license information:
5210
5225
 
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-OYXWKN46.js";
4
+ } from "./chunk-AIISNOCB.js";
5
5
  import {
6
6
  showHeartbeat,
7
7
  showSkill,
8
8
  showSkillJson
9
- } from "./chunk-RNNDFHTP.js";
9
+ } from "./chunk-AK26RTP5.js";
10
10
  import {
11
11
  REGISTRY_ABI,
12
12
  chainIdForCredentials,
@@ -15,8 +15,9 @@ import {
15
15
  loadCredentials,
16
16
  output,
17
17
  outputError,
18
- parseCommonFlags
19
- } from "./chunk-YYE3F3KA.js";
18
+ parseCommonFlags,
19
+ saveCredentials
20
+ } from "./chunk-AQHFWEZF.js";
20
21
 
21
22
  // src/cli/send.ts
22
23
  async function send(topicId, message, flags) {
@@ -170,11 +171,14 @@ async function whoami(appId, flags) {
170
171
  }
171
172
  }
172
173
  const creds = loadCredentials();
173
- if (creds) {
174
- const chainId = chainIdForCredentials(flags.chain);
175
- const chainCreds = creds.chains[chainId];
176
- result.ecdhRegistered = chainCreds?.ecdh?.registered ?? false;
177
- }
174
+ const chainId = chainIdForCredentials(flags.chain);
175
+ const chainCreds = creds?.chains[chainId];
176
+ const [ecdhRegistered, ecdhStored] = await Promise.all([
177
+ client.hasPublicKey(address).catch(() => false),
178
+ Promise.resolve(Boolean(chainCreds?.ecdh?.privateKey))
179
+ ]);
180
+ result.ecdhRegistered = ecdhRegistered;
181
+ result.ecdhStored = ecdhStored;
178
182
  if (json) {
179
183
  output(result, true);
180
184
  } else {
@@ -187,7 +191,9 @@ async function whoami(appId, flags) {
187
191
  console.log(`Member: ${result.isMember}`);
188
192
  console.log(`Agent: ${result.hasAgentIdentity}${result.agentTokenId ? ` (token #${result.agentTokenId})` : ""}`);
189
193
  }
190
- console.log(`ECDH: ${result.ecdhRegistered ? "registered" : "not registered"}`);
194
+ const ecdhStatus = result.ecdhRegistered ? "registered on-chain" : "not registered";
195
+ const ecdhStorage = result.ecdhStored ? "stored locally" : "not stored locally";
196
+ console.log(`ECDH: ${ecdhStatus} (${ecdhStorage})`);
191
197
  }
192
198
  }
193
199
 
@@ -793,13 +799,46 @@ async function keysRegister(flags) {
793
799
  await client.deriveECDHFromWallet();
794
800
  }
795
801
  if (!json) console.log("Registering ECDH public key on-chain...");
796
- const tx = await client.registerPublicKey();
797
- const receipt = await tx.wait();
802
+ let txHash = null;
803
+ let blockNumber;
804
+ let registeredOnChain = false;
805
+ try {
806
+ const tx = await client.registerPublicKey();
807
+ const receipt = await tx.wait();
808
+ txHash = tx.hash;
809
+ blockNumber = receipt?.blockNumber;
810
+ registeredOnChain = true;
811
+ } catch (error) {
812
+ if (!(error instanceof Error) || !error.message.includes("Public key already registered on-chain")) {
813
+ throw error;
814
+ }
815
+ registeredOnChain = true;
816
+ }
817
+ const keypair = client.getECDHKeypairHex();
818
+ if (creds && keypair) {
819
+ if (!creds.chains[chainId]) {
820
+ creds.chains[chainId] = {
821
+ name: flags.chain,
822
+ ecdh: null,
823
+ apps: {}
824
+ };
825
+ }
826
+ creds.chains[chainId].ecdh = {
827
+ privateKey: keypair.privateKey,
828
+ publicKey: keypair.publicKey,
829
+ registered: registeredOnChain
830
+ };
831
+ saveCredentials(creds);
832
+ }
798
833
  if (json) {
799
- output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, chain: flags.chain }, true);
834
+ output({ txHash, blockNumber, chain: flags.chain, registered: registeredOnChain }, true);
800
835
  } else {
801
- console.log(`TX: ${tx.hash}`);
802
- console.log(`Confirmed in block ${receipt?.blockNumber}`);
836
+ if (txHash) {
837
+ console.log(`TX: ${txHash}`);
838
+ console.log(`Confirmed in block ${blockNumber}`);
839
+ } else {
840
+ console.log("ECDH public key was already registered on-chain.");
841
+ }
803
842
  }
804
843
  }
805
844
  async function keysCheck(address, flags) {
@@ -1372,10 +1411,10 @@ async function resolveTopicId(client, {
1372
1411
  }
1373
1412
 
1374
1413
  // src/cli/index.ts
1375
- var VERSION = "0.12.6";
1414
+ var VERSION = "0.12.8";
1376
1415
  var HELP = `
1377
1416
  clawntenna v${VERSION}
1378
- On-chain encrypted messaging for AI agents
1417
+ Encrypted on-chain coordination for wallets, apps, and agents
1379
1418
 
1380
1419
  Usage:
1381
1420
  clawntenna <command> [options]
@@ -4,8 +4,8 @@ import {
4
4
  showHeartbeat,
5
5
  showSkill,
6
6
  showSkillJson
7
- } from "./chunk-RNNDFHTP.js";
8
- import "./chunk-YYE3F3KA.js";
7
+ } from "./chunk-AK26RTP5.js";
8
+ import "./chunk-AQHFWEZF.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-OYXWKN46.js";
6
- import "./chunk-YYE3F3KA.js";
5
+ } from "./chunk-AIISNOCB.js";
6
+ import "./chunk-AQHFWEZF.js";
7
7
  export {
8
8
  initState,
9
9
  stateInit
package/dist/index.cjs CHANGED
@@ -1647,6 +1647,16 @@ var Clawntenna = class _Clawntenna {
1647
1647
  this.ecdhPrivateKey = privateKey;
1648
1648
  this.ecdhPublicKey = publicKey;
1649
1649
  }
1650
+ /**
1651
+ * Export loaded ECDH keypair as hex strings for credential persistence.
1652
+ */
1653
+ getECDHKeypairHex() {
1654
+ if (!this.ecdhPrivateKey || !this.ecdhPublicKey) return null;
1655
+ return {
1656
+ privateKey: import_ethers.ethers.hexlify(this.ecdhPrivateKey),
1657
+ publicKey: import_ethers.ethers.hexlify(this.ecdhPublicKey)
1658
+ };
1659
+ }
1650
1660
  /**
1651
1661
  * Register ECDH public key on-chain.
1652
1662
  */