clawntenna 0.12.7 → 0.12.9

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.
@@ -3,7 +3,7 @@ import {
3
3
  CONFIG_DIR,
4
4
  loadCredentials,
5
5
  output
6
- } from "./chunk-7CKZWKH5.js";
6
+ } from "./chunk-S2IWR2YO.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.7",
24
+ skillVersion: "0.12.9",
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-7CKZWKH5.js";
4
+ } from "./chunk-S2IWR2YO.js";
5
5
 
6
6
  // src/cli/skill.ts
7
7
  import { readFileSync, existsSync, copyFileSync, mkdirSync } from "fs";
@@ -4446,16 +4446,22 @@ 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
  */
4452
4462
  async registerPublicKey() {
4453
4463
  this.requireSigner();
4454
4464
  if (!this.ecdhPublicKey) throw new Error("ECDH key not derived yet");
4455
- const hasKey = await this.keyManager.hasPublicKey(this.requireAddress());
4456
- if (hasKey) {
4457
- throw new Error("Public key already registered on-chain");
4458
- }
4459
4465
  return this.keyManager.registerPublicKey(this.ecdhPublicKey);
4460
4466
  }
4461
4467
  /**
@@ -4996,8 +5002,8 @@ function validateKeyAddress(creds) {
4996
5002
  }
4997
5003
  }
4998
5004
  async function runPostInit(address) {
4999
- const { initState } = await import("./state-W5B24KOL.js");
5000
- const { copySkillFiles } = await import("./skill-NMABLUB5.js");
5005
+ const { initState } = await import("./state-IT53LF3J.js");
5006
+ const { copySkillFiles } = await import("./skill-CTQZ7YJE.js");
5001
5007
  const stateResult = initState(address);
5002
5008
  const skillResult = copySkillFiles();
5003
5009
  return { stateResult, skillResult };
@@ -5139,6 +5145,10 @@ function loadCredentials() {
5139
5145
  }
5140
5146
  return null;
5141
5147
  }
5148
+ function saveCredentials(credentials) {
5149
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
5150
+ writeFileSync(CREDS_PATH, JSON.stringify(credentials, null, 2), { mode: 384 });
5151
+ }
5142
5152
 
5143
5153
  // src/cli/util.ts
5144
5154
  function parseCommonFlags(flags) {
@@ -5146,7 +5156,8 @@ function parseCommonFlags(flags) {
5146
5156
  chain: flags.chain ?? "base",
5147
5157
  key: flags.key,
5148
5158
  rpc: flags.rpc,
5149
- json: flags.json === "true"
5159
+ json: flags.json === "true",
5160
+ force: flags.force === "true"
5150
5161
  };
5151
5162
  }
5152
5163
  function loadClient(flags, requireWallet = true) {
@@ -5204,7 +5215,8 @@ export {
5204
5215
  chainIdForCredentials,
5205
5216
  CONFIG_DIR,
5206
5217
  init,
5207
- loadCredentials
5218
+ loadCredentials,
5219
+ saveCredentials
5208
5220
  };
5209
5221
  /*! Bundled license information:
5210
5222
 
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-T4XLWYPQ.js";
4
+ } from "./chunk-EOKSFPRZ.js";
5
5
  import {
6
6
  showHeartbeat,
7
7
  showSkill,
8
8
  showSkillJson
9
- } from "./chunk-QENCMD4F.js";
9
+ } from "./chunk-FDS4E5ME.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-7CKZWKH5.js";
18
+ parseCommonFlags,
19
+ saveCredentials
20
+ } from "./chunk-S2IWR2YO.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
 
@@ -786,6 +792,13 @@ async function keysRegister(flags) {
786
792
  const creds = loadCredentials();
787
793
  const chainId = chainIdForCredentials(flags.chain);
788
794
  const ecdhCreds = creds?.chains[chainId]?.ecdh;
795
+ const hadExistingRegistration = await client.hasPublicKey(client.address);
796
+ if (hadExistingRegistration && !flags.force) {
797
+ outputError(
798
+ "ECDH public key already registered on-chain. Re-run with --force to update it. Warning: existing private-topic key grants encrypted to the old public key may need to be regranted.",
799
+ json
800
+ );
801
+ }
789
802
  if (ecdhCreds?.privateKey) {
790
803
  client.loadECDHKeypair(ecdhCreds.privateKey);
791
804
  } else {
@@ -795,11 +808,40 @@ async function keysRegister(flags) {
795
808
  if (!json) console.log("Registering ECDH public key on-chain...");
796
809
  const tx = await client.registerPublicKey();
797
810
  const receipt = await tx.wait();
811
+ const txHash = tx.hash;
812
+ const blockNumber = receipt?.blockNumber;
813
+ const registeredOnChain = true;
814
+ const keypair = client.getECDHKeypairHex();
815
+ if (creds && keypair) {
816
+ if (!creds.chains[chainId]) {
817
+ creds.chains[chainId] = {
818
+ name: flags.chain,
819
+ ecdh: null,
820
+ apps: {}
821
+ };
822
+ }
823
+ creds.chains[chainId].ecdh = {
824
+ privateKey: keypair.privateKey,
825
+ publicKey: keypair.publicKey,
826
+ registered: registeredOnChain
827
+ };
828
+ saveCredentials(creds);
829
+ }
798
830
  if (json) {
799
- output({ txHash: tx.hash, blockNumber: receipt?.blockNumber, chain: flags.chain }, true);
831
+ output({
832
+ txHash,
833
+ blockNumber,
834
+ chain: flags.chain,
835
+ registered: registeredOnChain,
836
+ updated: hadExistingRegistration
837
+ }, true);
800
838
  } else {
801
- console.log(`TX: ${tx.hash}`);
802
- console.log(`Confirmed in block ${receipt?.blockNumber}`);
839
+ console.log(`TX: ${txHash}`);
840
+ console.log(`Confirmed in block ${blockNumber}`);
841
+ if (hadExistingRegistration) {
842
+ console.log("ECDH public key updated on-chain.");
843
+ console.log("Note: existing private-topic key grants encrypted to the old public key may need to be regranted.");
844
+ }
803
845
  }
804
846
  }
805
847
  async function keysCheck(address, flags) {
@@ -1372,7 +1414,7 @@ async function resolveTopicId(client, {
1372
1414
  }
1373
1415
 
1374
1416
  // src/cli/index.ts
1375
- var VERSION = "0.12.7";
1417
+ var VERSION = "0.12.9";
1376
1418
  var HELP = `
1377
1419
  clawntenna v${VERSION}
1378
1420
  Encrypted on-chain coordination for wallets, apps, and agents
@@ -1444,7 +1486,7 @@ var HELP = `
1444
1486
  schema publish <schemaId> "<body>" Publish new schema version
1445
1487
 
1446
1488
  ECDH / Private Topics:
1447
- keys register Register ECDH public key on-chain
1489
+ keys register [--force] Register ECDH public key on-chain
1448
1490
  keys check <address> Check if address has public key
1449
1491
  keys grant <topicId> <address> Grant key access to user
1450
1492
  keys revoke <topicId> <address> Revoke key access
@@ -1501,14 +1543,68 @@ var HELP = `
1501
1543
  Docs: https://clawntenna.com/docs
1502
1544
  `;
1503
1545
  var COMMAND_HELP = {
1546
+ init: "Usage: clawntenna init\n Creates wallet credentials, state, and synced skill files.",
1547
+ whoami: "Usage: clawntenna whoami [appId]\nOptions: --chain <name> --json\n Shows wallet, balance, nickname/member/agent info, and ECDH status.",
1504
1548
  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
1549
  read: 'Usage: clawntenna read <topicId>\n clawntenna read --app "<app>" --topic "<topic>"\nOptions: --limit <N> --json --chain <name>',
1506
1550
  subscribe: 'Usage: clawntenna subscribe <topicId>\n clawntenna subscribe --app "<app>" --topic "<topic>"\nOptions: --json --chain <name>',
1507
1551
  "app info": 'Usage: clawntenna app info <appId>\n clawntenna app info --app "<name>"',
1508
1552
  "app create": 'Usage: clawntenna app create --name "<name>" [--description "<desc>"] [--url <url>] [--public]\n clawntenna app create "<name>" "<desc>" [--url <url>] [--public]',
1553
+ "app update-url": 'Usage: clawntenna app update-url <appId> "<url>"',
1554
+ "app transfer-ownership": "Usage: clawntenna app transfer-ownership <appId> <newOwner>",
1555
+ "app accept-ownership": "Usage: clawntenna app accept-ownership <appId>",
1556
+ "app cancel-transfer": "Usage: clawntenna app cancel-transfer <appId>",
1557
+ "app pending-owner": "Usage: clawntenna app pending-owner <appId>",
1509
1558
  "topic info": 'Usage: clawntenna topic info <topicId>\n clawntenna topic info --app "<app>" --topic "<name>"',
1510
1559
  "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>"'
1560
+ "nickname set": 'Usage: clawntenna nickname set <appId> "<name>"',
1561
+ "nickname get": "Usage: clawntenna nickname get <appId> <address>",
1562
+ "nickname clear": "Usage: clawntenna nickname clear <appId>",
1563
+ members: "Usage: clawntenna members <appId>",
1564
+ "member info": "Usage: clawntenna member info <appId> <address>",
1565
+ "member add": 'Usage: clawntenna member add <appId> <address> "<nick>" [--roles N]',
1566
+ "member remove": "Usage: clawntenna member remove <appId> <address>",
1567
+ "member roles": "Usage: clawntenna member roles <appId> <address> <roles>",
1568
+ "permission set": "Usage: clawntenna permission set <topicId> <address> <level>",
1569
+ "permission get": "Usage: clawntenna permission get <topicId> <address>",
1570
+ "access check": "Usage: clawntenna access check <topicId> <address>",
1571
+ "agent register": "Usage: clawntenna agent register <appId> <tokenId>",
1572
+ "agent clear": "Usage: clawntenna agent clear <appId>",
1573
+ "agent info": "Usage: clawntenna agent info <appId> <address>",
1574
+ "schema create": 'Usage: clawntenna schema create <appId> "<name>" "<desc>" "<body>"',
1575
+ "schema info": "Usage: clawntenna schema info <schemaId>",
1576
+ "schema list": "Usage: clawntenna schema list <appId>",
1577
+ "schema bind": "Usage: clawntenna schema bind <topicId> <schemaId> <version>",
1578
+ "schema unbind": "Usage: clawntenna schema unbind <topicId>",
1579
+ "schema topic": "Usage: clawntenna schema topic <topicId>",
1580
+ "schema version": "Usage: clawntenna schema version <schemaId> <version>",
1581
+ "schema publish": 'Usage: clawntenna schema publish <schemaId> "<body>"',
1582
+ "keys register": "Usage: clawntenna keys register [--force]\n Registers your ECDH public key. Use --force to overwrite an existing on-chain key.",
1583
+ "keys check": "Usage: clawntenna keys check <address>",
1584
+ "keys grant": "Usage: clawntenna keys grant <topicId> <address>",
1585
+ "keys revoke": "Usage: clawntenna keys revoke <topicId> <address>",
1586
+ "keys rotate": "Usage: clawntenna keys rotate <topicId>",
1587
+ "keys has": "Usage: clawntenna keys has <topicId> <address>",
1588
+ "keys pending": "Usage: clawntenna keys pending <topicId>",
1589
+ topics: 'Usage: clawntenna topics <appId>\n clawntenna topics --app "<name>"',
1590
+ "fee topic-creation": "Usage: clawntenna fee topic-creation set <appId> <token> <amount>",
1591
+ "fee message": "Usage: clawntenna fee message set <topicId> <token> <amount>\n clawntenna fee message get <topicId>",
1592
+ "escrow inbox": "Usage: clawntenna escrow inbox <topicId>",
1593
+ "escrow enable": "Usage: clawntenna escrow enable <topicId> <timeout>",
1594
+ "escrow disable": "Usage: clawntenna escrow disable <topicId>",
1595
+ "escrow status": "Usage: clawntenna escrow status <topicId>",
1596
+ "escrow deposits": "Usage: clawntenna escrow deposits <topicId>",
1597
+ "escrow deposit": "Usage: clawntenna escrow deposit <depositId>",
1598
+ "escrow respond": "Usage: clawntenna escrow respond <topicId> <id1> [id2...] --payload 0x...",
1599
+ "escrow release": "Usage: clawntenna escrow release <depositId> [--ref N]",
1600
+ "escrow release-batch": "Usage: clawntenna escrow release-batch <id1> <id2> ...",
1601
+ "escrow refund": "Usage: clawntenna escrow refund <depositId>",
1602
+ "escrow refund-batch": "Usage: clawntenna escrow refund-batch <id1> <id2> ...",
1603
+ "escrow stats": "Usage: clawntenna escrow stats <address>",
1604
+ skill: "Usage: clawntenna skill\n Prints skill.md.",
1605
+ heartbeat: "Usage: clawntenna heartbeat\n Prints heartbeat.md.",
1606
+ "skill-json": "Usage: clawntenna skill-json\n Prints skill.json.",
1607
+ "state init": "Usage: clawntenna state init\n Initializes ~/.config/clawntenna/state.json."
1512
1608
  };
1513
1609
  function printCommandHelp(command, subcommand) {
1514
1610
  const key = subcommand ? `${command} ${subcommand}` : command;
@@ -1530,7 +1626,7 @@ function parseArgs(argv) {
1530
1626
  flags[key] = "true";
1531
1627
  i++;
1532
1628
  }
1533
- } else if (arg === "-h") {
1629
+ } else if (arg === "-h" || arg === "help") {
1534
1630
  flags["help"] = "true";
1535
1631
  i++;
1536
1632
  } else if (arg === "-v") {
@@ -1546,6 +1642,15 @@ function parseArgs(argv) {
1546
1642
  async function main() {
1547
1643
  const { command, args, flags } = parseArgs(process.argv.slice(2));
1548
1644
  const json = flags.json === "true";
1645
+ if (command === "help") {
1646
+ if (args.length === 0) {
1647
+ console.log(HELP);
1648
+ return;
1649
+ }
1650
+ const subcommand = args[1];
1651
+ printCommandHelp(args[0], subcommand);
1652
+ return;
1653
+ }
1549
1654
  if (flags.version) {
1550
1655
  console.log(VERSION);
1551
1656
  return;
@@ -1558,9 +1663,17 @@ async function main() {
1558
1663
  try {
1559
1664
  switch (command) {
1560
1665
  case "init":
1666
+ if (flags.help) {
1667
+ printCommandHelp("init");
1668
+ return;
1669
+ }
1561
1670
  await init(json);
1562
1671
  break;
1563
1672
  case "whoami": {
1673
+ if (flags.help) {
1674
+ printCommandHelp("whoami");
1675
+ return;
1676
+ }
1564
1677
  const appId = args[0] ? parseInt(args[0], 10) : null;
1565
1678
  await whoami(appId, cf);
1566
1679
  break;
@@ -1726,6 +1839,10 @@ async function main() {
1726
1839
  // --- Nicknames ---
1727
1840
  case "nickname": {
1728
1841
  const sub = args[0];
1842
+ if (flags.help) {
1843
+ printCommandHelp("nickname", sub);
1844
+ return;
1845
+ }
1729
1846
  if (sub === "set") {
1730
1847
  const appId = parseInt(args[1], 10);
1731
1848
  const name = args[2];
@@ -1747,6 +1864,10 @@ async function main() {
1747
1864
  }
1748
1865
  // --- Members ---
1749
1866
  case "members": {
1867
+ if (flags.help) {
1868
+ printCommandHelp("members");
1869
+ return;
1870
+ }
1750
1871
  const appId = parseInt(args[0], 10);
1751
1872
  if (isNaN(appId)) outputError("Usage: clawntenna members <appId>", json);
1752
1873
  await membersList(appId, cf);
@@ -1754,6 +1875,10 @@ async function main() {
1754
1875
  }
1755
1876
  case "member": {
1756
1877
  const sub = args[0];
1878
+ if (flags.help) {
1879
+ printCommandHelp("member", sub);
1880
+ return;
1881
+ }
1757
1882
  if (sub === "info") {
1758
1883
  const appId = parseInt(args[1], 10);
1759
1884
  const address = args[2];
@@ -1785,6 +1910,10 @@ async function main() {
1785
1910
  // --- Permissions ---
1786
1911
  case "permission": {
1787
1912
  const sub = args[0];
1913
+ if (flags.help) {
1914
+ printCommandHelp("permission", sub);
1915
+ return;
1916
+ }
1788
1917
  if (sub === "set") {
1789
1918
  const topicId = parseInt(args[1], 10);
1790
1919
  const address = args[2];
@@ -1803,6 +1932,10 @@ async function main() {
1803
1932
  }
1804
1933
  case "access": {
1805
1934
  const sub = args[0];
1935
+ if (flags.help) {
1936
+ printCommandHelp("access", sub);
1937
+ return;
1938
+ }
1806
1939
  if (sub === "check") {
1807
1940
  const topicId = parseInt(args[1], 10);
1808
1941
  const address = args[2];
@@ -1816,6 +1949,10 @@ async function main() {
1816
1949
  // --- Agent Identity ---
1817
1950
  case "agent": {
1818
1951
  const sub = args[0];
1952
+ if (flags.help) {
1953
+ printCommandHelp("agent", sub);
1954
+ return;
1955
+ }
1819
1956
  if (sub === "register") {
1820
1957
  const appId = parseInt(args[1], 10);
1821
1958
  const tokenId = parseInt(args[2], 10);
@@ -1838,6 +1975,10 @@ async function main() {
1838
1975
  // --- Schemas ---
1839
1976
  case "schema": {
1840
1977
  const sub = args[0];
1978
+ if (flags.help) {
1979
+ printCommandHelp("schema", sub);
1980
+ return;
1981
+ }
1841
1982
  if (sub === "create") {
1842
1983
  const appId = parseInt(args[1], 10);
1843
1984
  const name = args[2];
@@ -1885,6 +2026,10 @@ async function main() {
1885
2026
  // --- ECDH Keys ---
1886
2027
  case "keys": {
1887
2028
  const sub = args[0];
2029
+ if (flags.help) {
2030
+ printCommandHelp("keys", sub);
2031
+ return;
2032
+ }
1888
2033
  if (sub === "register") {
1889
2034
  await keysRegister(cf);
1890
2035
  } else if (sub === "check") {
@@ -1922,6 +2067,10 @@ async function main() {
1922
2067
  // --- Fees ---
1923
2068
  case "fee": {
1924
2069
  const sub = args[0];
2070
+ if (flags.help) {
2071
+ printCommandHelp("fee", sub);
2072
+ return;
2073
+ }
1925
2074
  if (sub === "topic-creation") {
1926
2075
  if (args[1] === "set") {
1927
2076
  const appId = parseInt(args[2], 10);
@@ -1954,6 +2103,10 @@ async function main() {
1954
2103
  // --- Escrow ---
1955
2104
  case "escrow": {
1956
2105
  const sub = args[0];
2106
+ if (flags.help) {
2107
+ printCommandHelp("escrow", sub);
2108
+ return;
2109
+ }
1957
2110
  if (sub === "inbox") {
1958
2111
  const topicId = parseInt(args[1], 10);
1959
2112
  if (isNaN(topicId)) outputError("Usage: clawntenna escrow inbox <topicId>", json);
@@ -2014,17 +2167,33 @@ async function main() {
2014
2167
  }
2015
2168
  // --- Skill Files ---
2016
2169
  case "skill":
2170
+ if (flags.help) {
2171
+ printCommandHelp("skill");
2172
+ return;
2173
+ }
2017
2174
  showSkill(json);
2018
2175
  break;
2019
2176
  case "heartbeat":
2177
+ if (flags.help) {
2178
+ printCommandHelp("heartbeat");
2179
+ return;
2180
+ }
2020
2181
  showHeartbeat(json);
2021
2182
  break;
2022
2183
  case "skill-json":
2184
+ if (flags.help) {
2185
+ printCommandHelp("skill-json");
2186
+ return;
2187
+ }
2023
2188
  showSkillJson();
2024
2189
  break;
2025
2190
  // --- State ---
2026
2191
  case "state": {
2027
2192
  const sub = args[0];
2193
+ if (flags.help) {
2194
+ printCommandHelp("state", sub);
2195
+ return;
2196
+ }
2028
2197
  if (sub === "init") {
2029
2198
  stateInit(json);
2030
2199
  } else {
@@ -4,8 +4,8 @@ import {
4
4
  showHeartbeat,
5
5
  showSkill,
6
6
  showSkillJson
7
- } from "./chunk-QENCMD4F.js";
8
- import "./chunk-7CKZWKH5.js";
7
+ } from "./chunk-FDS4E5ME.js";
8
+ import "./chunk-S2IWR2YO.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-T4XLWYPQ.js";
6
- import "./chunk-7CKZWKH5.js";
5
+ } from "./chunk-EOKSFPRZ.js";
6
+ import "./chunk-S2IWR2YO.js";
7
7
  export {
8
8
  initState,
9
9
  stateInit
package/dist/index.cjs CHANGED
@@ -1647,16 +1647,22 @@ 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
  */
1653
1663
  async registerPublicKey() {
1654
1664
  this.requireSigner();
1655
1665
  if (!this.ecdhPublicKey) throw new Error("ECDH key not derived yet");
1656
- const hasKey = await this.keyManager.hasPublicKey(this.requireAddress());
1657
- if (hasKey) {
1658
- throw new Error("Public key already registered on-chain");
1659
- }
1660
1666
  return this.keyManager.registerPublicKey(this.ecdhPublicKey);
1661
1667
  }
1662
1668
  /**