@alfe.ai/gateway 0.10.3 → 0.10.5

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.
Files changed (2) hide show
  1. package/dist/health.js +45 -4
  2. package/package.json +2 -2
package/dist/health.js CHANGED
@@ -6115,7 +6115,7 @@ var ReconciliationEngine = class {
6115
6115
  log$4.info(`Reinstalling ${id} on version change (reinstall requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt}): ${local.version} → ${desired.version}`);
6116
6116
  this.manager.resetReinstallAttempts(id);
6117
6117
  this.activateAttempts.delete(id);
6118
- if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
6118
+ if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
6119
6119
  report.installed.push(id);
6120
6120
  report.activated.push(id);
6121
6121
  report.results.push({
@@ -6160,7 +6160,7 @@ var ReconciliationEngine = class {
6160
6160
  log$4.info(`Reinstalling ${id} from error state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
6161
6161
  this.manager.resetReinstallAttempts(id);
6162
6162
  this.activateAttempts.delete(id);
6163
- if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
6163
+ if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
6164
6164
  report.installed.push(id);
6165
6165
  report.activated.push(id);
6166
6166
  report.results.push({
@@ -6270,7 +6270,7 @@ var ReconciliationEngine = class {
6270
6270
  log$4.info(`Reinstalling ${id} from stale installing state (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
6271
6271
  this.manager.resetReinstallAttempts(id);
6272
6272
  this.activateAttempts.delete(id);
6273
- if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
6273
+ if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
6274
6274
  report.installed.push(id);
6275
6275
  report.activated.push(id);
6276
6276
  report.results.push({
@@ -6345,7 +6345,7 @@ var ReconciliationEngine = class {
6345
6345
  log$4.info(`Reinstall requested for ${id} (requested: ${desired.reinstallRequestedAt}, installed: ${local.installedAt})`);
6346
6346
  this.manager.resetReinstallAttempts(id);
6347
6347
  this.activateAttempts.delete(id);
6348
- if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource)).configApplied) report.configApplied = true;
6348
+ if ((await this.manager.reinstall(id, desired.version, desired.config, desired.customSource, { reinstallRequestedAt: desired.reinstallRequestedAt })).configApplied) report.configApplied = true;
6349
6349
  report.installed.push(id);
6350
6350
  report.activated.push(id);
6351
6351
  report.results.push({
@@ -7633,9 +7633,13 @@ async function stopExistingDaemon() {
7633
7633
  * Each connected plugin registers with its name/version and receives
7634
7634
  * commands from the daemon (forwarded from cloud).
7635
7635
  */
7636
+ /** Same-PID re-registrations inside this window before the flap warning fires. */
7637
+ const SAME_PID_FLAP_WINDOW_MS = 6e4;
7636
7638
  var IPCServer = class {
7637
7639
  server = null;
7638
7640
  connections = /* @__PURE__ */ new Map();
7641
+ samePidReplacements = /* @__PURE__ */ new Map();
7642
+ samePidFlapWarnedAt = /* @__PURE__ */ new Map();
7639
7643
  socketPath;
7640
7644
  requestHandler = null;
7641
7645
  constructor(socketPath, daemonVersion = "unknown") {
@@ -7696,6 +7700,8 @@ var IPCServer = class {
7696
7700
  });
7697
7701
  for (const conn of this.connections.values()) conn.socket.destroy();
7698
7702
  this.connections.clear();
7703
+ this.samePidReplacements.clear();
7704
+ this.samePidFlapWarnedAt.clear();
7699
7705
  });
7700
7706
  }
7701
7707
  /**
@@ -7871,6 +7877,40 @@ var IPCServer = class {
7871
7877
  }, "IPC: unhandled message");
7872
7878
  }
7873
7879
  }
7880
+ /**
7881
+ * A plugin that keeps re-registering from the SAME process is two live
7882
+ * clients in one process evicting each other (two module instances of
7883
+ * the plugin, each with its own socket), not a reconnect after a fault.
7884
+ * Each eviction fails every request in flight on the evicted socket, so
7885
+ * the symptom downstream is "Connection closed" on any call longer than
7886
+ * the client's reconnect backoff (Wibke Jnr, 2026-09-21: 2 evictions/s,
7887
+ * every per-business MYOB call dead). The dedupe above is still right
7888
+ * (a genuinely stale socket must be dropped), so make the flap loud
7889
+ * instead: one warning per plugin per minute once it exceeds the rate.
7890
+ */
7891
+ noteSamePidReplacement(name, pid) {
7892
+ const key = `${name}:${String(pid)}`;
7893
+ const now = Date.now();
7894
+ for (const [k, ts] of this.samePidReplacements) {
7895
+ if (k === key) continue;
7896
+ if (now - (ts[ts.length - 1] ?? 0) >= 6e4) {
7897
+ this.samePidReplacements.delete(k);
7898
+ this.samePidFlapWarnedAt.delete(k);
7899
+ }
7900
+ }
7901
+ const times = (this.samePidReplacements.get(key) ?? []).filter((t) => now - t < SAME_PID_FLAP_WINDOW_MS);
7902
+ times.push(now);
7903
+ this.samePidReplacements.set(key, times);
7904
+ if (times.length < 10) return;
7905
+ if (now - (this.samePidFlapWarnedAt.get(key) ?? 0) < 6e4) return;
7906
+ this.samePidFlapWarnedAt.set(key, now);
7907
+ logger.warn({
7908
+ plugin: name,
7909
+ pid,
7910
+ replacements: times.length,
7911
+ windowMs: SAME_PID_FLAP_WINDOW_MS
7912
+ }, "IPC: plugin is re-registering from the same process in a loop — two IPC clients in one process are evicting each other; in-flight requests fail with \"Connection closed\"");
7913
+ }
7874
7914
  closeOversizedConnection(conn) {
7875
7915
  logger.warn({
7876
7916
  connId: conn.id,
@@ -7923,6 +7963,7 @@ var IPCServer = class {
7923
7963
  plugin: name,
7924
7964
  pid
7925
7965
  }, "IPC: closing stale duplicate connection (same PID)");
7966
+ this.noteSamePidReplacement(name, pid);
7926
7967
  existing.socket.destroy();
7927
7968
  staleIds.push(existingId);
7928
7969
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/gateway",
3
- "version": "0.10.3",
3
+ "version": "0.10.5",
4
4
  "description": "Alfe local gateway daemon — persistent control plane for agent integrations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,7 +27,7 @@
27
27
  "@alfe.ai/ai-proxy-local": "^0.0.17",
28
28
  "@alfe.ai/config": "^0.4.1",
29
29
  "@alfe.ai/integration-manifest": "^0.4.2",
30
- "@alfe.ai/integrations": "^0.6.8",
30
+ "@alfe.ai/integrations": "^0.6.11",
31
31
  "@alfe.ai/mcp-bundler": "^0.4.2"
32
32
  },
33
33
  "license": "UNLICENSED",