@highway1/cli 0.1.23 → 0.1.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highway1/cli",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "CLI tool for Clawiverse network",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@ export function registerJoinCommand(program: Command): void {
16
16
  .command('join')
17
17
  .description('Join the Clawiverse network')
18
18
  .option('--bootstrap <peers...>', 'Bootstrap peer addresses')
19
+ .option('--relay', 'Run as a relay server and advertise relay capability')
19
20
  .action(async (options) => {
20
21
  try {
21
22
  printHeader('Join Clawiverse Network');
@@ -42,6 +43,7 @@ export function registerJoinCommand(program: Command): void {
42
43
  bootstrapPeers,
43
44
  enableDHT: true,
44
45
  reserveRelaySlot: true,
46
+ enableRelay: options.relay ?? false,
45
47
  });
46
48
 
47
49
  await node.start();
@@ -54,31 +56,50 @@ export function registerJoinCommand(program: Command): void {
54
56
 
55
57
  // Wait for bootstrap peer connection before publishing to DHT
56
58
  const connectSpin = spinner('Connecting to bootstrap peers...');
59
+
60
+ // Phase 1: wait for peer:connect (up to 10s)
57
61
  await new Promise<void>((resolve) => {
58
62
  const timeout = setTimeout(resolve, 10000);
59
63
  node.libp2p.addEventListener('peer:connect', () => {
60
64
  clearTimeout(timeout);
61
- setTimeout(resolve, 500);
65
+ resolve();
62
66
  }, { once: true });
63
67
  });
68
+
69
+ // Phase 2: wait for relay address to appear (up to 5s)
70
+ const hasRelayAddr = () => node.getMultiaddrs().some(a => a.includes('/p2p-circuit'));
71
+ if (!hasRelayAddr()) {
72
+ await new Promise<void>((resolve) => {
73
+ const timeout = setTimeout(resolve, 5000);
74
+ const check = () => {
75
+ if (hasRelayAddr()) { clearTimeout(timeout); resolve(); }
76
+ };
77
+ node.libp2p.addEventListener('self:multiaddress:updated', check);
78
+ setTimeout(() => node.libp2p.removeEventListener('self:multiaddress:updated', check), 5000);
79
+ });
80
+ }
81
+
64
82
  connectSpin.succeed('Connected to network!');
65
83
 
66
84
  // Publish Agent Card with peerId and multiaddrs so others can dial us
67
85
  const cardSpin = spinner('Publishing Agent Card to DHT...');
68
86
 
69
- // Include circuit relay addresses so nodes behind NAT can be reached
70
- const directAddrs = node.getMultiaddrs();
71
- const relayAddrs = bootstrapPeers.map((relayAddr) => {
72
- const peerId = node.getPeerId();
73
- return `${relayAddr}/p2p-circuit/p2p/${peerId}`;
74
- });
75
- const allAddrs = [...directAddrs, ...relayAddrs];
87
+ // Use relay addresses from libp2p (populated after reservation), fall back to manual construction
88
+ const directAddrs = node.getMultiaddrs().filter(a => !a.includes('/p2p-circuit'));
89
+ const relayAddrs = node.getMultiaddrs().filter(a => a.includes('/p2p-circuit'));
90
+ const fallbackRelayAddrs = relayAddrs.length === 0
91
+ ? bootstrapPeers.map(r => `${r}/p2p-circuit/p2p/${node.getPeerId()}`)
92
+ : [];
93
+ const allAddrs = [...directAddrs, ...relayAddrs, ...fallbackRelayAddrs];
94
+
95
+ const capabilities = [...(card.capabilities ?? [])];
96
+ if (options.relay) capabilities.push('relay');
76
97
 
77
98
  const agentCard = createAgentCard(
78
99
  identity.did,
79
100
  card.name,
80
101
  card.description,
81
- card.capabilities,
102
+ capabilities,
82
103
  allAddrs,
83
104
  node.getPeerId()
84
105
  );
@@ -92,6 +113,17 @@ export function registerJoinCommand(program: Command): void {
92
113
 
93
114
  cardSpin.succeed('Agent Card published!');
94
115
 
116
+ // Keep connection alive by pinging bootstrap peers periodically
117
+ const pingInterval = setInterval(async () => {
118
+ for (const peer of node.libp2p.getPeers()) {
119
+ try {
120
+ await node.libp2p.services.ping.ping(peer);
121
+ } catch {
122
+ // ignore ping failures
123
+ }
124
+ }
125
+ }, 30000); // ping every 30s
126
+
95
127
  // Register message handlers for incoming messages
96
128
  const router = createMessageRouter(
97
129
  node.libp2p,
@@ -162,6 +194,7 @@ export function registerJoinCommand(program: Command): void {
162
194
  process.on('SIGINT', async () => {
163
195
  console.log();
164
196
  const stopSpin = spinner('Stopping node...');
197
+ clearInterval(pingInterval);
165
198
  await router.stop();
166
199
  await node.stop();
167
200
  stopSpin.succeed('Node stopped');
@@ -55,10 +55,19 @@ export function registerSendCommand(program: Command): void {
55
55
  enableDHT: true,
56
56
  });
57
57
 
58
+ // Register peer:identify listener BEFORE start() so we don't miss the event
59
+ const identifyDone = new Promise<void>((resolve) => {
60
+ const timeout = setTimeout(resolve, 12000);
61
+ node.libp2p.addEventListener('peer:identify', () => {
62
+ clearTimeout(timeout);
63
+ setTimeout(resolve, 500);
64
+ }, { once: true });
65
+ });
66
+
58
67
  await node.start();
59
68
 
60
69
  spin.text = 'Connecting to network...';
61
- await new Promise((resolve) => setTimeout(resolve, 3000));
70
+ await identifyDone;
62
71
 
63
72
  const dht = createDHTOperations(node.libp2p);
64
73