cross-agent-teams-mcp 0.5.10 → 0.5.12

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/dist/cli.d.ts CHANGED
@@ -6,6 +6,7 @@ interface DaemonCliArgs {
6
6
  requestedPort: number;
7
7
  host: string;
8
8
  localDevice: string;
9
+ loopbackCompanion: boolean;
9
10
  }
10
11
  declare function parseDaemonCliArgs(argv?: readonly string[], env?: NodeJS.ProcessEnv): DaemonCliArgs;
11
12
 
package/dist/cli.js CHANGED
@@ -8,6 +8,7 @@ import { fileURLToPath } from "url";
8
8
 
9
9
  // src/daemon/server.ts
10
10
  import Fastify from "fastify";
11
+ import { createServer as createHttpServer } from "http";
11
12
 
12
13
  // src/storage/db.ts
13
14
  import Database from "better-sqlite3";
@@ -3712,12 +3713,14 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
3712
3713
  sessionIdGenerator: () => randomUUID5(),
3713
3714
  onsessioninitialized: (sid) => {
3714
3715
  sessionIdForCaller = sid;
3716
+ const now2 = Date.now();
3715
3717
  sessions.set(sid, {
3716
3718
  transport,
3717
3719
  server,
3718
3720
  sessionId: sid,
3719
3721
  agentIdHolder,
3720
- createdAt: Date.now(),
3722
+ createdAt: now2,
3723
+ lastActivityAt: now2,
3721
3724
  clientInfo: void 0,
3722
3725
  originInfo: { origin: "local", remote_addr: null }
3723
3726
  });
@@ -3771,12 +3774,14 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
3771
3774
  registerSvc
3772
3775
  );
3773
3776
  server.connect(transport);
3777
+ const now = Date.now();
3774
3778
  return {
3775
3779
  transport,
3776
3780
  server,
3777
3781
  sessionId: "",
3778
3782
  agentIdHolder,
3779
- createdAt: Date.now(),
3783
+ createdAt: now,
3784
+ lastActivityAt: now,
3780
3785
  originInfo: { origin: "local", remote_addr: null }
3781
3786
  };
3782
3787
  }
@@ -3820,6 +3825,7 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
3820
3825
  }
3821
3826
  if (session) {
3822
3827
  session.originInfo = originInfo;
3828
+ session.lastActivityAt = Date.now();
3823
3829
  }
3824
3830
  if (body?.method === "initialize") {
3825
3831
  const params = body.params;
@@ -3842,6 +3848,7 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
3842
3848
  const sid = req.headers["mcp-session-id"];
3843
3849
  const session = sid ? sessions.get(sid) : void 0;
3844
3850
  if (!session) return reply.code(400).send({ error: "unknown_session" });
3851
+ session.lastActivityAt = Date.now();
3845
3852
  await session.transport.handleRequest(req.raw, reply.raw);
3846
3853
  return reply;
3847
3854
  });
@@ -3849,14 +3856,15 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
3849
3856
  const sid = req.headers["mcp-session-id"];
3850
3857
  const session = sid ? sessions.get(sid) : void 0;
3851
3858
  if (!session) return reply.code(400).send({ error: "unknown_session" });
3859
+ session.lastActivityAt = Date.now();
3852
3860
  await session.transport.handleRequest(req.raw, reply.raw);
3853
3861
  return reply;
3854
3862
  });
3855
- function reapOrphanSessions(now, graceMs = 6e4) {
3863
+ function reapOrphanSessions(now, graceMs = 18e5) {
3856
3864
  for (const session of sessions.values()) {
3857
3865
  if (session.agentIdHolder.current !== void 0) continue;
3858
- const ageMs = now - session.createdAt;
3859
- if (ageMs < graceMs) continue;
3866
+ const idleMs = now - session.lastActivityAt;
3867
+ if (idleMs < graceMs) continue;
3860
3868
  try {
3861
3869
  void session.transport.close();
3862
3870
  } catch {
@@ -4036,10 +4044,14 @@ function classifyPeerAddress(address) {
4036
4044
  function isLoopbackHost(host) {
4037
4045
  return host === "localhost" || isLoopbackAddress(host);
4038
4046
  }
4047
+ function bindHostCoversIpv4Loopback(host) {
4048
+ return host === "127.0.0.1" || host === "localhost" || host === "0.0.0.0";
4049
+ }
4039
4050
 
4040
4051
  // src/daemon/server.ts
4041
4052
  var DEFAULT_KEEP_ALIVE_TIMEOUT_MS = 12e4;
4042
4053
  var DEFAULT_ORPHAN_GC_INTERVAL_MS = 6e4;
4054
+ var DEFAULT_ORPHAN_GC_IDLE_MS = 18e5;
4043
4055
  function parsePositiveInt(raw, fallback) {
4044
4056
  const n = Number(raw);
4045
4057
  return Number.isInteger(n) && n > 0 ? n : fallback;
@@ -4073,9 +4085,10 @@ async function buildServer(opts) {
4073
4085
  }, cleanupIntervalMs);
4074
4086
  if (typeof interval.unref === "function") interval.unref();
4075
4087
  const orphanGcIntervalMs = opts.orphanGcIntervalMs ?? parsePositiveInt(process.env.ORPHAN_GC_INTERVAL_MS, DEFAULT_ORPHAN_GC_INTERVAL_MS);
4088
+ const orphanGcIdleMs = opts.orphanGcIdleMs ?? parsePositiveInt(process.env.ORPHAN_GC_IDLE_MS, DEFAULT_ORPHAN_GC_IDLE_MS);
4076
4089
  const orphanGcInterval = setInterval(() => {
4077
4090
  try {
4078
- mcp.reapOrphanSessions(Date.now());
4091
+ mcp.reapOrphanSessions(Date.now(), orphanGcIdleMs);
4079
4092
  } catch {
4080
4093
  }
4081
4094
  }, orphanGcIntervalMs);
@@ -4092,10 +4105,46 @@ async function buildServer(opts) {
4092
4105
  async function startServer(opts) {
4093
4106
  const app = await buildServer(opts);
4094
4107
  const host = opts.host ?? "127.0.0.1";
4108
+ const companionRef = { server: void 0 };
4109
+ app.addHook("onClose", async () => {
4110
+ const server = companionRef.server;
4111
+ if (!server) return;
4112
+ await new Promise((resolve) => {
4113
+ server.close(() => resolve());
4114
+ });
4115
+ });
4095
4116
  await app.listen({ port: opts.port, host });
4096
4117
  const addr = app.server.address();
4097
4118
  const port = addr && typeof addr === "object" ? addr.port : opts.port;
4098
- return { app, port, host };
4119
+ const companionEnabled = opts.loopbackCompanion !== false;
4120
+ if (companionEnabled && !bindHostCoversIpv4Loopback(host)) {
4121
+ const handler = app.server.listeners("request")[0];
4122
+ if (!handler) {
4123
+ await app.close();
4124
+ throw new Error("loopback_companion_no_handler: Fastify did not expose a request handler");
4125
+ }
4126
+ const companion = createHttpServer(handler);
4127
+ try {
4128
+ await new Promise((resolve, reject) => {
4129
+ const onErr = (err) => reject(err);
4130
+ companion.once("error", onErr);
4131
+ companion.listen(port, "127.0.0.1", () => {
4132
+ companion.removeListener("error", onErr);
4133
+ resolve();
4134
+ });
4135
+ });
4136
+ } catch (err) {
4137
+ try {
4138
+ companion.close();
4139
+ } catch {
4140
+ }
4141
+ await app.close();
4142
+ const detail = err instanceof Error ? err.message : String(err);
4143
+ throw new Error(`loopback_companion_bind_failed: ${detail}`);
4144
+ }
4145
+ companionRef.server = companion;
4146
+ }
4147
+ return { app, port, host, loopbackCompanion: companionRef.server };
4099
4148
  }
4100
4149
 
4101
4150
  // src/daemon/pid.ts
@@ -4130,17 +4179,76 @@ function releasePidFile(path) {
4130
4179
  }
4131
4180
 
4132
4181
  // src/daemon/shutdown.ts
4133
- function wireShutdown(app, pidPath) {
4134
- const handler = async (_signal) => {
4182
+ var DEFAULT_GRACE_MS = 5e3;
4183
+ function resolveGraceMs(explicit) {
4184
+ if (typeof explicit === "number" && Number.isFinite(explicit)) {
4185
+ return explicit < 0 ? 0 : Math.floor(explicit);
4186
+ }
4187
+ const raw = process.env.XATS_SHUTDOWN_GRACE_MS;
4188
+ if (raw === void 0 || raw === "") return DEFAULT_GRACE_MS;
4189
+ const n = Number(raw);
4190
+ if (!Number.isFinite(n)) return DEFAULT_GRACE_MS;
4191
+ return n < 0 ? 0 : Math.floor(n);
4192
+ }
4193
+ function wireShutdown(app, pidPath, opts = {}) {
4194
+ const graceMs = resolveGraceMs(opts.graceMs);
4195
+ const exit = opts.exit ?? ((code) => {
4196
+ process.exit(code);
4197
+ });
4198
+ const extraForceClose = opts.extraForceClose;
4199
+ let shuttingDown = false;
4200
+ const handler = (_signal) => {
4201
+ if (shuttingDown) {
4202
+ releasePidFile(pidPath);
4203
+ exit(0);
4204
+ return;
4205
+ }
4206
+ shuttingDown = true;
4207
+ void runDrain(app, pidPath, graceMs, exit, extraForceClose);
4208
+ };
4209
+ process.on("SIGTERM", handler);
4210
+ process.on("SIGINT", handler);
4211
+ }
4212
+ function forceCloseAll(app, extra) {
4213
+ try {
4214
+ app.server.closeAllConnections();
4215
+ } catch {
4216
+ }
4217
+ if (extra) {
4218
+ try {
4219
+ extra();
4220
+ } catch {
4221
+ }
4222
+ }
4223
+ }
4224
+ async function runDrain(app, pidPath, graceMs, exit, extraForceClose) {
4225
+ if (graceMs <= 0) {
4226
+ forceCloseAll(app, extraForceClose);
4135
4227
  try {
4136
4228
  await app.close();
4137
4229
  } catch {
4138
4230
  }
4139
4231
  releasePidFile(pidPath);
4140
- process.exit(0);
4141
- };
4142
- process.once("SIGTERM", handler);
4143
- process.once("SIGINT", handler);
4232
+ exit(0);
4233
+ return;
4234
+ }
4235
+ let timer;
4236
+ const deadline = new Promise((resolve) => {
4237
+ timer = setTimeout(() => resolve("timeout"), graceMs);
4238
+ if (typeof timer.unref === "function") timer.unref();
4239
+ });
4240
+ const closed = app.close().then(() => "closed").catch(() => "closed");
4241
+ const winner = await Promise.race([closed, deadline]);
4242
+ if (winner === "timeout") {
4243
+ forceCloseAll(app, extraForceClose);
4244
+ try {
4245
+ await app.close();
4246
+ } catch {
4247
+ }
4248
+ }
4249
+ if (timer) clearTimeout(timer);
4250
+ releasePidFile(pidPath);
4251
+ exit(0);
4144
4252
  }
4145
4253
 
4146
4254
  // src/daemon/port.ts
@@ -4179,13 +4287,15 @@ function parseDaemonCliArgs(argv = process.argv, env = process.env) {
4179
4287
  const host = parseArg("--host", "127.0.0.1") ?? "127.0.0.1";
4180
4288
  const localDevice = resolveLocalDeviceLabel(parseArg("--device"));
4181
4289
  const requestedPort = Number(parseArg("--port", "9100"));
4290
+ const loopbackCompanion = !process.argv.includes("--no-loopback-companion");
4182
4291
  return {
4183
4292
  pidPath: parseArg("--pid-file", join(home, "daemon.pid")),
4184
4293
  dbPath: parseArg("--db", join(home, "data.db")),
4185
4294
  token,
4186
4295
  requestedPort,
4187
4296
  host,
4188
- localDevice
4297
+ localDevice,
4298
+ loopbackCompanion
4189
4299
  };
4190
4300
  } finally {
4191
4301
  process.argv = originalArgv;
@@ -4209,10 +4319,20 @@ async function runDaemon() {
4209
4319
  token: args.token,
4210
4320
  port,
4211
4321
  host: args.host,
4212
- localDevice: args.localDevice
4322
+ localDevice: args.localDevice,
4323
+ loopbackCompanion: args.loopbackCompanion
4324
+ });
4325
+ const companion = started.loopbackCompanion;
4326
+ wireShutdown(started.app, args.pidPath, {
4327
+ extraForceClose: companion ? () => {
4328
+ try {
4329
+ companion.closeAllConnections();
4330
+ } catch {
4331
+ }
4332
+ } : void 0
4213
4333
  });
4214
- wireShutdown(started.app, args.pidPath);
4215
- console.log(`listening on ${started.host}:${started.port} device=${args.localDevice}`);
4334
+ const companionSuffix = companion ? ` (+ 127.0.0.1:${started.port} loopback companion)` : "";
4335
+ console.log(`listening on ${started.host}:${started.port}${companionSuffix} device=${args.localDevice}`);
4216
4336
  }
4217
4337
  function resolveDaemonPort(explicit) {
4218
4338
  if (explicit !== void 0) {