@datafrog-io/n2n-nexus 0.3.1 → 0.3.2

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/build/config.js CHANGED
@@ -104,70 +104,74 @@ async function probeHost(port) {
104
104
  * 3. If not found, try to become Host
105
105
  * 4. If bind fails, wait and re-probe (give winner time to start)
106
106
  */
107
- async function isHostAutoElection(root, retryCount = 0) {
107
+ async function isHostAutoElection(root) {
108
108
  const startPort = 5688;
109
109
  const endPort = 5800;
110
- // Phase 1: Probe-First - Check if any Host already exists (Concurrent Batch Scan)
111
- const BATCH_SIZE = 20;
112
- for (let batchStart = startPort; batchStart <= endPort; batchStart += BATCH_SIZE) {
113
- const batchEnd = Math.min(batchStart + BATCH_SIZE - 1, endPort);
114
- const promises = [];
115
- for (let port = batchStart; port <= batchEnd; port++) {
116
- promises.push(probeHost(port).then(res => ({ port, ...res })));
110
+ let retryCount = 0;
111
+ // eslint-disable-next-line no-constant-condition
112
+ while (true) {
113
+ // Phase 1: Probe-First - Check if any Host already exists (Concurrent Batch Scan)
114
+ const BATCH_SIZE = 20;
115
+ for (let batchStart = startPort; batchStart <= endPort; batchStart += BATCH_SIZE) {
116
+ const batchEnd = Math.min(batchStart + BATCH_SIZE - 1, endPort);
117
+ const promises = [];
118
+ for (let port = batchStart; port <= batchEnd; port++) {
119
+ promises.push(probeHost(port).then(res => ({ port, ...res })));
120
+ }
121
+ const results = await Promise.all(promises);
122
+ const found = results.find(r => r.isNexus);
123
+ if (found) {
124
+ return { isHost: false, port: found.port, rootStorage: found.rootStorage };
125
+ }
117
126
  }
118
- const results = await Promise.all(promises);
119
- const found = results.find(r => r.isNexus);
120
- if (found) {
121
- return { isHost: false, port: found.port, rootStorage: found.rootStorage };
122
- }
123
- }
124
- // Phase 2: No Host found, attempt to become Host
125
- for (let port = startPort; port <= endPort; port++) {
126
- const result = await new Promise((resolve) => {
127
- const server = http.createServer((req, res) => {
128
- if (req.url === "/hello") {
129
- res.writeHead(200, { "Content-Type": "application/json" });
130
- res.end(JSON.stringify({
131
- service: "n2n-nexus",
132
- role: "host",
133
- version: pkg.version,
134
- rootStorage: root
135
- }));
136
- return;
137
- }
138
- res.writeHead(404);
139
- res.end();
140
- });
141
- server.on("error", (err) => {
142
- if (err.code === "EADDRINUSE") {
143
- resolve({ isHost: false });
144
- }
145
- else {
146
- resolve({ isHost: false });
147
- }
148
- });
149
- server.listen(port, "127.0.0.1", () => {
150
- resolve({ isHost: true, server });
127
+ // Phase 2: No Host found, attempt to become Host
128
+ for (let port = startPort; port <= endPort; port++) {
129
+ const result = await new Promise((resolve) => {
130
+ const server = http.createServer((req, res) => {
131
+ if (req.url === "/hello") {
132
+ res.writeHead(200, { "Content-Type": "application/json" });
133
+ res.end(JSON.stringify({
134
+ service: "n2n-nexus",
135
+ role: "host",
136
+ version: pkg.version,
137
+ rootStorage: root
138
+ }));
139
+ return;
140
+ }
141
+ res.writeHead(404);
142
+ res.end();
143
+ });
144
+ server.on("error", (err) => {
145
+ if (err.code === "EADDRINUSE") {
146
+ resolve({ isHost: false });
147
+ }
148
+ else {
149
+ resolve({ isHost: false });
150
+ }
151
+ });
152
+ server.listen(port, "127.0.0.1", () => {
153
+ resolve({ isHost: true, server });
154
+ });
151
155
  });
152
- });
153
- if (result.isHost) {
154
- return { isHost: true, port, server: result.server };
155
- }
156
- // Phase 3: Bind failed - another Guest won. Wait then join winner.
157
- await new Promise(r => setTimeout(r, 10000)); // Give winner 10s to start /hello
158
- const probe = await probeHost(port);
159
- if (probe.isNexus) {
160
- return { isHost: false, port, rootStorage: probe.rootStorage };
156
+ if (result.isHost) {
157
+ return { isHost: true, port, server: result.server };
158
+ }
159
+ // Phase 3: Bind failed - another Guest won. Wait then join winner.
160
+ await new Promise(r => setTimeout(r, 10000)); // Give winner 10s to start /hello
161
+ const probe = await probeHost(port);
162
+ if (probe.isNexus) {
163
+ return { isHost: false, port, rootStorage: probe.rootStorage };
164
+ }
165
+ // If still not Nexus, try next port (occupied by non-Nexus service)
161
166
  }
162
- // If still not Nexus, try next port (occupied by non-Nexus service)
167
+ // Fallback: All ports occupied - progressive backoff retry
168
+ // First 5 attempts: 1 minute interval, then 2 minute interval
169
+ const waitTime = retryCount < 5 ? 60000 : 120000;
170
+ const intervalStr = retryCount < 5 ? "1 minute" : "2 minutes";
171
+ console.error(`[Nexus] All ports ${startPort}-${endPort} occupied. Retry #${retryCount + 1} in ${intervalStr}...`);
172
+ await new Promise(r => setTimeout(r, waitTime));
173
+ retryCount++;
163
174
  }
164
- // Fallback: All ports occupied - progressive backoff retry
165
- // First 5 attempts: 1 minute interval, then 2 minute interval
166
- const waitTime = retryCount < 5 ? 60000 : 120000;
167
- const intervalStr = retryCount < 5 ? "1 minute" : "2 minutes";
168
- console.error(`[Nexus] All ports ${startPort}-${endPort} occupied. Retry #${retryCount + 1} in ${intervalStr}...`);
169
- await new Promise(r => setTimeout(r, waitTime));
170
- return isHostAutoElection(root, retryCount + 1);
171
175
  }
172
176
  /**
173
177
  * Automatic Project Name Detection
package/build/index.js CHANGED
@@ -231,7 +231,7 @@ class NexusServer {
231
231
  const req = http.request({
232
232
  hostname: "127.0.0.1",
233
233
  port: CONFIG.port,
234
- path: `/mcp?sessionId=${sessionId}&id=${guestId}`,
234
+ path: `/mcp?sessionId=${sessionId}&id=${encodeURIComponent(guestId)}`,
235
235
  method: "POST",
236
236
  headers: { "Content-Type": "application/json" }
237
237
  });
@@ -243,7 +243,7 @@ class NexusServer {
243
243
  catch { /* suppress */ }
244
244
  };
245
245
  process.stdin.on("data", stdioHandler);
246
- http.get(`http://127.0.0.1:${CONFIG.port}/mcp?id=${guestId}`, (res) => {
246
+ http.get(`http://127.0.0.1:${CONFIG.port}/mcp?id=${encodeURIComponent(guestId)}`, (res) => {
247
247
  retryCount = 0; // Reset on successful connection
248
248
  let buffer = "";
249
249
  res.on("data", (chunk) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datafrog-io/n2n-nexus",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Modular MCP Server for multi-AI assistant coordination",
5
5
  "main": "build/index.js",
6
6
  "type": "module",