a2acalling 0.6.22 → 0.6.24

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/bin/cli.js CHANGED
@@ -407,15 +407,36 @@ async function resolveInviteHostname() {
407
407
  try {
408
408
  const { A2AConfig } = require('../src/lib/config');
409
409
  const config = new A2AConfig();
410
+ const agent = config.getAgent() || {};
411
+ const onboarding = config.getAll().onboarding || {};
412
+
413
+ // If hostname is set without a port (e.g., "149.28.213.47"), assume port 80
414
+ // (user configured reverse proxy or direct bind to 80)
415
+ // If hostname has a port (e.g., "149.28.213.47:3007"), use that port
416
+ // If no hostname set, use server_port from onboarding
417
+ const hostname = agent.hostname || '';
418
+ const hasExplicitPort = hostname.includes(':') && !hostname.startsWith('[');
419
+
420
+ let defaultPort;
421
+ if (hasExplicitPort) {
422
+ defaultPort = null; // Will be parsed from hostname
423
+ } else if (hostname && !hostname.includes('localhost')) {
424
+ // External hostname without port = assume port 80 (reverse proxy or direct)
425
+ defaultPort = 80;
426
+ } else {
427
+ // Local or no hostname - use actual server port
428
+ defaultPort = onboarding.server_port || process.env.PORT || process.env.A2A_PORT || 80;
429
+ }
430
+
410
431
  const resolved = await resolveInviteHost({
411
432
  config,
412
- defaultPort: process.env.PORT || process.env.A2A_PORT || 3001
433
+ defaultPort
413
434
  });
414
435
  return resolved;
415
436
  } catch (err) {
416
437
  return resolveInviteHost({
417
438
  fallbackHost: process.env.OPENCLAW_HOSTNAME || process.env.HOSTNAME || 'localhost',
418
- defaultPort: process.env.PORT || process.env.A2A_PORT || 3001
439
+ defaultPort: process.env.PORT || process.env.A2A_PORT || 80
419
440
  });
420
441
  }
421
442
  }
@@ -1126,6 +1147,40 @@ https://github.com/onthegonow/a2a_calling`;
1126
1147
  }
1127
1148
  },
1128
1149
 
1150
+ config: (args) => {
1151
+ const { A2AConfig } = require('../src/lib/config');
1152
+ const config = new A2AConfig();
1153
+
1154
+ const hostname = args.flags.hostname || args.flags.h;
1155
+ const port = args.flags.port || args.flags.p;
1156
+ const show = args.flags.show || args.flags.s || (!hostname && !port);
1157
+
1158
+ if (show) {
1159
+ const agent = config.getAgent();
1160
+ console.log('A2A Configuration:\n');
1161
+ console.log(` Hostname: ${agent.hostname || '(not set)'}`);
1162
+ console.log(` Name: ${agent.name || '(not set)'}`);
1163
+ console.log(` Description: ${agent.description || '(not set)'}`);
1164
+ const onboarding = config.getAll().onboarding || {};
1165
+ console.log(` Server port: ${onboarding.server_port || '(not running)'}`);
1166
+ console.log(` Onboarding step: ${onboarding.step || 'not started'}`);
1167
+ return;
1168
+ }
1169
+
1170
+ const updates = {};
1171
+ if (hostname) {
1172
+ // Remove port from hostname if it's :80 (default)
1173
+ const cleanHostname = hostname.replace(/:80$/, '');
1174
+ updates.hostname = cleanHostname;
1175
+ console.log(` Hostname updated to: ${cleanHostname}`);
1176
+ }
1177
+
1178
+ if (Object.keys(updates).length > 0) {
1179
+ config.setAgent(updates);
1180
+ console.log(' ✅ Configuration saved.');
1181
+ }
1182
+ },
1183
+
1129
1184
  server: (args) => {
1130
1185
  const explicitPort = args.flags.port || args.flags.p || process.env.PORT;
1131
1186
  if (explicitPort) {
@@ -1220,15 +1275,28 @@ https://github.com/onthegonow/a2a_calling`;
1220
1275
  }
1221
1276
 
1222
1277
  // ── Step 1: Port selection ───────────────────────────────────────────
1223
- // Scan ports 80, 3001-3020 and pick the first available one.
1224
- // Only show the selected port agents don't need to see every candidate.
1225
- // Interactive users can override with a custom port; non-interactive
1226
- // auto-accepts the recommendation.
1278
+ // Port 80 is strongly preferred (no firewall config needed for external access).
1279
+ // If port 80 is available and bindable, use it. Otherwise fall back to 3001-3020.
1227
1280
  printSection('Port Configuration');
1228
1281
  const preferredPort = parsePort(args.flags.port || args.flags.p, null);
1229
1282
  const candidates = await inspectPorts(preferredPort);
1230
1283
  const availableCandidates = candidates.filter(c => c.available);
1231
- const recommendedPort = availableCandidates.length ? availableCandidates[0].port : null;
1284
+
1285
+ // Strongly prefer port 80 if available
1286
+ const port80Candidate = candidates.find(c => c.port === 80);
1287
+ const port80Available = port80Candidate && port80Candidate.available;
1288
+
1289
+ let recommendedPort;
1290
+ if (port80Available) {
1291
+ recommendedPort = 80;
1292
+ console.log(' Port 80 is available — using it for easiest external access.');
1293
+ } else if (availableCandidates.length) {
1294
+ recommendedPort = availableCandidates[0].port;
1295
+ console.log(` Port 80 is in use. Using fallback port ${recommendedPort}.`);
1296
+ console.log(' (Reverse proxy or firewall config will be needed for external access.)');
1297
+ } else {
1298
+ recommendedPort = null;
1299
+ }
1232
1300
 
1233
1301
  if (!recommendedPort) {
1234
1302
  console.error(' Could not find a bindable port in the scan range.');
@@ -1240,9 +1308,14 @@ https://github.com/onthegonow/a2a_calling`;
1240
1308
  process.exit(1);
1241
1309
  }
1242
1310
 
1243
- console.log(` Selected port: ${recommendedPort}`);
1244
1311
  let serverPort = recommendedPort;
1245
- const portChoice = await promptText(`Use port ${recommendedPort}? [Y/n/custom]: `, 'y');
1312
+
1313
+ // If we got port 80, just confirm briefly. Otherwise allow override.
1314
+ const portPrompt = port80Available
1315
+ ? `Use port 80? [Y/n]: `
1316
+ : `Use port ${recommendedPort}? [Y/n/custom]: `;
1317
+ const portChoice = await promptText(portPrompt, 'y');
1318
+
1246
1319
  if (!interactive) {
1247
1320
  serverPort = recommendedPort;
1248
1321
  } else if (!['', 'y', 'Y', 'yes', 'YES', 'ye'].includes(String(portChoice).trim())) {
@@ -1389,77 +1462,64 @@ https://github.com/onthegonow/a2a_calling`;
1389
1462
  console.log(' ✅ A2A server is running');
1390
1463
 
1391
1464
  if (externalIp) {
1392
- const verifyUrl = `http://${publicHost}/api/a2a/ping`;
1393
- if (serverPort !== 80) {
1394
- // Check what's using port 80 and detect web servers
1395
- const port80Status = await isPortListening(80, '127.0.0.1', { timeoutMs: 250 });
1465
+ if (serverPort === 80) {
1466
+ // Port 80 — optimal setup, no extra config needed
1467
+ console.log(`\n ✅ Running on port 80 external agents can reach you directly.`);
1468
+ console.log(` Invite hostname: ${externalIp}`);
1469
+ // Update publicHost to not include port since 80 is default
1470
+ publicHost = externalIp;
1471
+ } else {
1472
+ // Not on port 80 — need reverse proxy or firewall config
1396
1473
  const { spawnSync } = require('child_process');
1397
1474
  const hasNginx = spawnSync('which', ['nginx'], { encoding: 'utf8' }).status === 0;
1398
1475
  const hasCaddy = spawnSync('which', ['caddy'], { encoding: 'utf8' }).status === 0;
1399
- const hasSudo = spawnSync('sudo', ['-n', 'true'], { encoding: 'utf8' }).status === 0;
1400
1476
 
1401
- console.log(`\n ━━━ IMPORTANT: Port Configuration ━━━`);
1402
- console.log(`\n A2A works best on port 80. Other ports require firewall configuration.`);
1403
- console.log(` Current: A2A server on port ${serverPort}`);
1477
+ console.log(`\n ━━━ IMPORTANT: External Access Configuration ━━━`);
1478
+ console.log(`\n A2A server is on port ${serverPort}, but external callers expect port 80.`);
1479
+ console.log(` Port 80 is in use by ${hasNginx ? 'nginx' : hasCaddy ? 'Caddy' : 'another web server'}.`);
1480
+ console.log(`\n RECOMMENDED: Configure reverse proxy to route /api/a2a/* to port ${serverPort}`);
1404
1481
 
1405
- if (port80Status.listening) {
1406
- console.log(` Port 80: IN USE (likely ${hasNginx ? 'nginx' : hasCaddy ? 'caddy' : 'a web server'})`);
1407
- console.log(`\n RECOMMENDED: Configure reverse proxy to route /api/a2a/* from port 80 to ${serverPort}`);
1408
- console.log(` This is the easiest option — no firewall changes needed.\n`);
1409
-
1410
- if (hasNginx) {
1411
- console.log(` ── nginx config (add to /etc/nginx/sites-available/default) ──`);
1412
- console.log(` location /api/a2a/ {`);
1413
- console.log(` proxy_pass http://127.0.0.1:${serverPort}/api/a2a/;`);
1414
- console.log(` proxy_http_version 1.1;`);
1415
- console.log(` proxy_set_header Host $host;`);
1416
- console.log(` proxy_set_header X-Real-IP $remote_addr;`);
1417
- console.log(` }`);
1418
- console.log(` ────────────────────────────────────────────────────────────`);
1419
- console.log(`\n To apply: sudo nano /etc/nginx/sites-available/default`);
1420
- console.log(` (add the location block inside your server {})`);
1421
- console.log(` sudo nginx -t && sudo systemctl reload nginx`);
1422
- }
1423
-
1424
- if (hasCaddy) {
1425
- console.log(`\n ── Caddy config ──`);
1426
- console.log(` handle /api/a2a/* {`);
1427
- console.log(` reverse_proxy 127.0.0.1:${serverPort}`);
1428
- console.log(` }`);
1429
- console.log(` ───────────────────`);
1430
- }
1431
-
1432
- console.log(`\n After configuring, your invite hostname should be: ${externalIp}`);
1433
- console.log(` (port 80 is the default, so no port number needed)`);
1434
-
1435
- } else {
1436
- console.log(` Port 80: AVAILABLE`);
1437
- console.log(`\n RECOMMENDED: Restart A2A on port 80 for easiest setup.`);
1438
- console.log(` Run: a2a stop && a2a start --port 80`);
1439
- if (!hasSudo) {
1440
- console.log(` Note: Port 80 may require sudo. Try: sudo a2a start --port 80`);
1441
- }
1482
+ if (hasNginx) {
1483
+ console.log(`\n ── nginx config (add to /etc/nginx/sites-available/default) ──`);
1484
+ console.log(` location /api/a2a/ {`);
1485
+ console.log(` proxy_pass http://127.0.0.1:${serverPort}/api/a2a/;`);
1486
+ console.log(` proxy_http_version 1.1;`);
1487
+ console.log(` proxy_set_header Host $host;`);
1488
+ console.log(` proxy_set_header X-Real-IP $remote_addr;`);
1489
+ console.log(` }`);
1490
+ console.log(` ────────────────────────────────────────────────────────────`);
1491
+ console.log(`\n To apply:`);
1492
+ console.log(` sudo nano /etc/nginx/sites-available/default`);
1493
+ console.log(` (add the location block inside your server {})`);
1494
+ console.log(` sudo nginx -t && sudo systemctl reload nginx`);
1442
1495
  }
1443
1496
 
1444
- console.log(`\n ── Alternative: Firewall route (more complex) ──`);
1445
- console.log(` If you can't use port 80, open port ${serverPort} in your firewall:`);
1497
+ if (hasCaddy) {
1498
+ console.log(`\n ── Caddy config ──`);
1499
+ console.log(` handle /api/a2a/* {`);
1500
+ console.log(` reverse_proxy 127.0.0.1:${serverPort}`);
1501
+ console.log(` }`);
1502
+ console.log(` ───────────────────`);
1503
+ }
1504
+
1505
+ console.log(`\n After reverse proxy is configured:`);
1506
+ console.log(` Invite hostname will be: ${externalIp} (no port needed)`);
1507
+
1508
+ console.log(`\n ── Alternative: Direct firewall access ──`);
1509
+ console.log(` Open port ${serverPort} directly (less recommended):`);
1446
1510
  console.log(` sudo ufw allow ${serverPort}/tcp # Ubuntu/Debian`);
1447
- console.log(` sudo firewall-cmd --add-port=${serverPort}/tcp --permanent && sudo firewall-cmd --reload # RHEL/CentOS`);
1448
- console.log(` Your invite will then use: ${publicHost}`);
1511
+ console.log(` Invite hostname will then be: ${publicHost}`);
1449
1512
 
1450
1513
  console.log(`\n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
1451
- console.log(`\n AGENT ACTION: Configure ingress for A2A to receive external calls.`);
1452
- console.log(` Options (in order of preference):`);
1453
- if (!port80Status.listening) {
1454
- console.log(` 1. Restart on port 80: a2a stop && ${hasSudo ? '' : 'sudo '}a2a start --port 80`);
1455
- console.log(` 2. Configure firewall for port ${serverPort} (see commands above)`);
1456
- } else {
1457
- console.log(` 1. Add reverse proxy config to ${hasNginx ? 'nginx' : hasCaddy ? 'Caddy' : 'your web server'} (see above)`);
1458
- console.log(` 2. Configure firewall for port ${serverPort} (see commands above)`);
1459
- }
1460
- console.log(` 3. Skip (local-only testing, external agents won't be able to call)`);
1461
- console.log(`\n Ask your owner which option to proceed with.`);
1514
+ console.log(`\n AGENT ACTION REQUIRED:`);
1515
+ console.log(` 1. Configure reverse proxy (recommended) add nginx/caddy config above`);
1516
+ console.log(` 2. OR open firewall for port ${serverPort} — run the ufw/firewall-cmd command`);
1517
+ console.log(` 3. OR skip (local-only, external agents cannot call)`);
1518
+ console.log(`\n Ask your owner which option to use, then proceed.`);
1519
+ console.log(` If reverse proxy is configured, update hostname with: a2a config --hostname ${externalIp}`);
1462
1520
  }
1521
+
1522
+ const verifyUrl = `http://${publicHost}/api/a2a/ping`;
1463
1523
  console.log(`\n Verify: curl -s ${verifyUrl}`);
1464
1524
  }
1465
1525
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.6.22",
3
+ "version": "0.6.24",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -74,7 +74,8 @@ function formatHostPort(hostname, port) {
74
74
  if (!host) return '';
75
75
  const needsBrackets = net.isIP(host) === 6;
76
76
  const hostPart = needsBrackets ? `[${host}]` : host;
77
- return p ? `${hostPart}:${p}` : hostPart;
77
+ // Don't append port 80 (it's the default HTTP port)
78
+ return (p && p !== 80) ? `${hostPart}:${p}` : hostPart;
78
79
  }
79
80
 
80
81
  function isPrivateIpv4(ip) {