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 +129 -69
- package/package.json +1 -1
- package/src/lib/invite-host.js +2 -1
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
|
|
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 ||
|
|
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
|
-
//
|
|
1224
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
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:
|
|
1402
|
-
console.log(`\n A2A
|
|
1403
|
-
console.log(`
|
|
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 (
|
|
1406
|
-
console.log(
|
|
1407
|
-
console.log(
|
|
1408
|
-
console.log(`
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
-
|
|
1445
|
-
|
|
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(`
|
|
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
|
|
1452
|
-
console.log(`
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
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
package/src/lib/invite-host.js
CHANGED
|
@@ -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
|
-
|
|
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) {
|