a2acalling 0.6.47 → 0.6.48

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
@@ -1954,6 +1954,19 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
1954
1954
  return;
1955
1955
  }
1956
1956
 
1957
+ // Pre-start cleanup: kill any existing a2a server from a previous run
1958
+ try {
1959
+ const { killExistingServer } = require('../src/lib/pid-file');
1960
+ const cleanup = killExistingServer();
1961
+ if (cleanup.killed) {
1962
+ console.log(` Stopped previous server (PID ${cleanup.pid}).`);
1963
+ // Brief pause to let the port free up
1964
+ await new Promise(r => setTimeout(r, 500));
1965
+ }
1966
+ } catch (e) {
1967
+ // Best effort — continue with startup
1968
+ }
1969
+
1957
1970
  const isAlreadyListening = await isPortListening(serverPort, '127.0.0.1', { timeoutMs: 250 });
1958
1971
  let serverPid = null;
1959
1972
  if (!isAlreadyListening.listening) {
@@ -1987,7 +2000,15 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
1987
2000
 
1988
2001
  if (serverPid) {
1989
2002
  console.log(' Server started.');
1990
- config.setOnboarding({ server_pid: serverPid, server_port: serverPort });
2003
+ const existingPids = (config.getOnboarding().server_pids || []).filter(p => {
2004
+ try { process.kill(p, 0); return true; } catch (e) { return false; }
2005
+ });
2006
+ if (!existingPids.includes(serverPid)) existingPids.push(serverPid);
2007
+ config.setOnboarding({
2008
+ server_pid: serverPid,
2009
+ server_pids: existingPids,
2010
+ server_port: serverPort
2011
+ });
1991
2012
  } else {
1992
2013
  console.log(' Using existing server.');
1993
2014
  }
@@ -2318,24 +2339,43 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
2318
2339
  }
2319
2340
  }
2320
2341
 
2321
- // Kill server by PID from config (detached process started by quickstart)
2342
+ // Kill server by PID from config and PID file (detached process started by quickstart)
2322
2343
  // Then verify the port is actually freed; if not, find and kill whatever holds it.
2323
2344
  async function killServerPid() {
2324
- let pid, serverPort;
2345
+ let pid, serverPort, serverPids = [];
2325
2346
  try {
2326
2347
  const { A2AConfig } = require('../src/lib/config');
2327
2348
  const cfg = new A2AConfig();
2328
2349
  const onboarding = cfg.getOnboarding();
2329
2350
  pid = onboarding.server_pid;
2330
2351
  serverPort = onboarding.server_port;
2352
+ serverPids = Array.isArray(onboarding.server_pids) ? onboarding.server_pids : [];
2331
2353
  } catch (err) {
2332
- // Config read failed — not fatal, continue with pm2 path
2333
- return { ok: true, skipped: true };
2354
+ // Config read failed — not fatal, continue
2334
2355
  }
2335
2356
 
2336
- // Step 1: Try to kill the PID from config
2337
- if (pid) {
2338
- killPidSync(pid);
2357
+ // Step 0: Try PID file first (most reliable source)
2358
+ try {
2359
+ const { readPidFile, removePidFile } = require('../src/lib/pid-file');
2360
+ const filePid = readPidFile();
2361
+ if (filePid) {
2362
+ killPidSync(filePid);
2363
+ removePidFile();
2364
+ // If config PID is the same, don't double-kill
2365
+ if (filePid === pid) pid = null;
2366
+ }
2367
+ } catch (e) {
2368
+ // pid-file module load failed — continue with config PID
2369
+ }
2370
+
2371
+ // Step 1: Try to kill all tracked PIDs from config
2372
+ const allPids = new Set();
2373
+ if (pid) allPids.add(pid);
2374
+ for (const p of serverPids) {
2375
+ if (typeof p === 'number' && p > 0) allPids.add(p);
2376
+ }
2377
+ for (const p of allPids) {
2378
+ killPidSync(p);
2339
2379
  }
2340
2380
 
2341
2381
  // Step 2: Verify the port is freed
@@ -2359,7 +2399,13 @@ a2a add "${inviteUrl}" "${ownerText || 'friend'}" && a2a call "${ownerText || 'f
2359
2399
  }
2360
2400
  }
2361
2401
 
2362
- return { ok: true, pid, port: serverPort, skipped: !pid };
2402
+ // Clean up PID file if it still exists
2403
+ try {
2404
+ const { removePidFile } = require('../src/lib/pid-file');
2405
+ removePidFile();
2406
+ } catch (e) {}
2407
+
2408
+ return { ok: true, pid, port: serverPort, skipped: !pid && allPids.size === 0 };
2363
2409
  }
2364
2410
 
2365
2411
  process.stdout.write('Stopping server... ');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.6.47",
3
+ "version": "0.6.48",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/server.js CHANGED
@@ -22,6 +22,7 @@ const {
22
22
  } = require('./lib/prompt-template');
23
23
  const { findAvailablePort } = require('./lib/port-scanner');
24
24
  const { createLogger } = require('./lib/logger');
25
+ const { writePidFile, removePidFile } = require('./lib/pid-file');
25
26
 
26
27
  const DEFAULT_PORTS = [80, 3001, 8080, 8443, 9001];
27
28
  const requestedPort = process.env.PORT ? parseInt(process.env.PORT, 10)
@@ -903,6 +904,7 @@ async function startServer() {
903
904
  features: ['adaptive collaboration', 'auto-contacts', 'summaries', 'dashboard']
904
905
  }
905
906
  });
907
+ writePidFile(process.pid);
906
908
  });
907
909
 
908
910
  server.on('error', (err) => {
@@ -917,6 +919,16 @@ async function startServer() {
917
919
  }
918
920
  throw err;
919
921
  });
922
+
923
+ // Graceful shutdown: clean up PID file
924
+ function shutdown() {
925
+ removePidFile();
926
+ server.close(() => process.exit(0));
927
+ // Force exit after 5s if connections won't close
928
+ setTimeout(() => process.exit(0), 5000).unref();
929
+ }
930
+ process.on('SIGTERM', shutdown);
931
+ process.on('SIGINT', shutdown);
920
932
  }
921
933
 
922
934
  startServer();