@dainprotocol/tunnel 1.1.6 → 1.1.7

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.
@@ -136,6 +136,7 @@ class DainTunnel extends events_1.EventEmitter {
136
136
  });
137
137
  }
138
138
  handleMessage(message, resolve) {
139
+ console.log(`[Tunnel Client] Received message type: ${message.type}`);
139
140
  switch (message.type) {
140
141
  case "tunnelUrl":
141
142
  this.tunnelUrl = message.url;
@@ -149,14 +150,18 @@ class DainTunnel extends events_1.EventEmitter {
149
150
  this.handleWebSocketConnection(message);
150
151
  break;
151
152
  case "sse_connection":
153
+ console.log(`[Tunnel Client] Routing to handleSSEConnection`);
152
154
  this.handleSSEConnection(message);
153
155
  break;
154
156
  case "sse_close":
157
+ console.log(`[Tunnel Client] Routing to handleSSEClose`);
155
158
  this.handleSSEClose(message);
156
159
  break;
157
160
  case "websocket":
158
161
  this.handleWebSocketMessage(message);
159
162
  break;
163
+ default:
164
+ console.log(`[Tunnel Client] Unknown message type: ${message.type}`);
160
165
  }
161
166
  }
162
167
  async handleRequest(request) {
@@ -250,6 +255,11 @@ class DainTunnel extends events_1.EventEmitter {
250
255
  }
251
256
  }
252
257
  handleSSEConnection(message) {
258
+ console.log(`[Tunnel Client] ====== SSE CONNECTION HANDLER START ======`);
259
+ console.log(`[Tunnel Client] SSE ID: ${message.id}`);
260
+ console.log(`[Tunnel Client] Path: ${message.path}`);
261
+ console.log(`[Tunnel Client] Method: ${message.method}`);
262
+ console.log(`[Tunnel Client] Local port: ${this.port}`);
253
263
  try {
254
264
  // Create an EventSource-like stream to the local server
255
265
  // Since Node.js doesn't have a built-in EventSource, we'll use HTTP
@@ -260,8 +270,13 @@ class DainTunnel extends events_1.EventEmitter {
260
270
  method: message.method || 'GET', // Use provided method (supports POST tool calls)
261
271
  headers: message.headers,
262
272
  };
273
+ console.log(`[Tunnel Client] Creating HTTP request to: http://localhost:${this.port}${message.path}`);
274
+ console.log(`[Tunnel Client] Request headers:`, JSON.stringify(message.headers, null, 2));
263
275
  const req = http_1.default.request(options, (res) => {
276
+ console.log(`[Tunnel Client] Response received with status: ${res.statusCode}`);
277
+ console.log(`[Tunnel Client] Response headers:`, JSON.stringify(res.headers, null, 2));
264
278
  if (res.statusCode !== 200) {
279
+ console.error(`[Tunnel Client] ❌ Non-200 status code: ${res.statusCode}`);
265
280
  if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
266
281
  this.ws.send(JSON.stringify({
267
282
  type: 'sse',
@@ -272,17 +287,22 @@ class DainTunnel extends events_1.EventEmitter {
272
287
  }
273
288
  return;
274
289
  }
290
+ console.log(`[Tunnel Client] ✓ Starting SSE stream processing for ${message.id}`);
275
291
  // Process SSE stream
276
292
  let buffer = '';
293
+ let eventCount = 0;
277
294
  res.on('data', (chunk) => {
278
295
  const chunkStr = chunk.toString();
279
- console.log(`[Tunnel Client] SSE chunk received (${chunkStr.length} bytes):`, chunkStr.substring(0, 200));
296
+ console.log(`[Tunnel Client] SSE chunk ${++eventCount} received (${chunkStr.length} bytes)`);
297
+ console.log(`[Tunnel Client] Chunk preview:`, chunkStr.substring(0, 200));
280
298
  buffer += chunkStr;
281
299
  // Process complete SSE messages
300
+ let messagesProcessed = 0;
282
301
  while (buffer.includes('\n\n')) {
283
302
  const messageEndIndex = buffer.indexOf('\n\n');
284
303
  const messageData = buffer.substring(0, messageEndIndex);
285
304
  buffer = buffer.substring(messageEndIndex + 2);
305
+ console.log(`[Tunnel Client] Processing SSE message ${++messagesProcessed}:`, messageData);
286
306
  // Parse the SSE message
287
307
  const lines = messageData.split('\n');
288
308
  let event = 'message';
@@ -300,18 +320,27 @@ class DainTunnel extends events_1.EventEmitter {
300
320
  data = data.substring(0, data.length - 1);
301
321
  }
302
322
  // Forward to server
303
- console.log(`[Tunnel Client] Forwarding SSE event: ${event}, data length: ${data.length}`);
323
+ console.log(`[Tunnel Client] Forwarding SSE event to tunnel server:`);
324
+ console.log(`[Tunnel Client] SSE ID: ${message.id}`);
325
+ console.log(`[Tunnel Client] Event: ${event}`);
326
+ console.log(`[Tunnel Client] Data length: ${data.length}`);
304
327
  if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
305
- this.ws.send(JSON.stringify({
328
+ const forwardMessage = {
306
329
  type: 'sse',
307
330
  id: message.id,
308
331
  event,
309
332
  data
310
- }));
333
+ };
334
+ this.ws.send(JSON.stringify(forwardMessage));
335
+ console.log(`[Tunnel Client] ✓ SSE event forwarded to tunnel server`);
336
+ }
337
+ else {
338
+ console.error(`[Tunnel Client] ❌ Cannot forward SSE event - WebSocket not open`);
311
339
  }
312
340
  }
313
341
  });
314
342
  res.on('end', () => {
343
+ console.log(`[Tunnel Client] SSE stream ended for ${message.id}`);
315
344
  if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
316
345
  this.ws.send(JSON.stringify({
317
346
  type: 'sse',
@@ -319,10 +348,15 @@ class DainTunnel extends events_1.EventEmitter {
319
348
  event: 'close',
320
349
  data: 'Connection closed'
321
350
  }));
351
+ console.log(`[Tunnel Client] Sent close event to tunnel server`);
322
352
  }
323
353
  });
354
+ res.on('error', (error) => {
355
+ console.error(`[Tunnel Client] Response stream error for ${message.id}:`, error);
356
+ });
324
357
  });
325
358
  req.on('error', (error) => {
359
+ console.error(`[Tunnel Client] ❌ Request error for ${message.id}:`, error);
326
360
  if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
327
361
  this.ws.send(JSON.stringify({
328
362
  type: 'sse',
@@ -334,15 +368,20 @@ class DainTunnel extends events_1.EventEmitter {
334
368
  });
335
369
  // Write request body for POST requests
336
370
  if (message.body && message.method !== 'GET') {
337
- req.write(Buffer.from(message.body, 'base64'));
371
+ const bodyBuffer = Buffer.from(message.body, 'base64');
372
+ console.log(`[Tunnel Client] Writing request body (${bodyBuffer.length} bytes)`);
373
+ req.write(bodyBuffer);
338
374
  }
339
375
  req.end();
376
+ console.log(`[Tunnel Client] Request sent to local service`);
340
377
  // Store a reference to abort the connection later if needed
341
378
  this.sseClients.set(message.id, req);
379
+ console.log(`[Tunnel Client] SSE client stored. Total SSE clients: ${this.sseClients.size}`);
342
380
  this.emit("sse_connection", { id: message.id, path: message.path });
381
+ console.log(`[Tunnel Client] ====== SSE CONNECTION HANDLER COMPLETE ======`);
343
382
  }
344
383
  catch (error) {
345
- console.error('Error establishing SSE connection:', error);
384
+ console.error(`[Tunnel Client] CRITICAL ERROR establishing SSE connection:`, error);
346
385
  if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
347
386
  this.ws.send(JSON.stringify({
348
387
  type: 'sse',
@@ -354,11 +393,17 @@ class DainTunnel extends events_1.EventEmitter {
354
393
  }
355
394
  }
356
395
  handleSSEClose(message) {
396
+ console.log(`[Tunnel Client] SSE close requested for: ${message.id}`);
357
397
  const client = this.sseClients.get(message.id);
358
398
  if (client) {
399
+ console.log(`[Tunnel Client] Destroying SSE client: ${message.id}`);
359
400
  // Abort the request if it's still active
360
401
  client.destroy();
361
402
  this.sseClients.delete(message.id);
403
+ console.log(`[Tunnel Client] SSE client destroyed. Remaining: ${this.sseClients.size}`);
404
+ }
405
+ else {
406
+ console.log(`[Tunnel Client] SSE client not found: ${message.id}`);
362
407
  }
363
408
  }
364
409
  forwardRequest(request) {
@@ -300,21 +300,54 @@ class DainTunnelServer {
300
300
  }
301
301
  }
302
302
  handleSSEMessage(data) {
303
+ var _a;
304
+ console.log(`[Tunnel Server] ====== RECEIVED SSE MESSAGE ======`);
305
+ console.log(`[Tunnel Server] SSE ID: ${data.id}`);
306
+ console.log(`[Tunnel Server] Event: ${data.event}`);
307
+ console.log(`[Tunnel Server] Data length: ${((_a = data.data) === null || _a === void 0 ? void 0 : _a.length) || 0}`);
308
+ console.log(`[Tunnel Server] Active SSE connections: ${this.sseConnections.size}`);
309
+ console.log(`[Tunnel Server] Active SSE IDs: ${Array.from(this.sseConnections.keys()).join(', ')}`);
303
310
  const connection = this.sseConnections.get(data.id);
304
311
  if (!connection) {
305
- console.log(`SSE connection not found: ${data.id}`);
312
+ console.error(`[Tunnel Server] ❌ SSE connection not found: ${data.id}`);
313
+ console.error(`[Tunnel Server] Available connections: ${Array.from(this.sseConnections.keys()).join(', ')}`);
314
+ console.error(`[Tunnel Server] This means either:`);
315
+ console.error(`[Tunnel Server] 1. The connection was never created (handleSSERequest not called)`);
316
+ console.error(`[Tunnel Server] 2. The connection was already closed`);
317
+ console.error(`[Tunnel Server] 3. There's an ID mismatch`);
306
318
  return;
307
319
  }
320
+ console.log(`[Tunnel Server] ✓ SSE connection found for ${data.id}`);
308
321
  const { res } = connection;
309
- if (data.event) {
310
- res.write(`event: ${data.event}\n`);
322
+ try {
323
+ if (data.event) {
324
+ res.write(`event: ${data.event}\n`);
325
+ console.log(`[Tunnel Server] Wrote event: ${data.event}`);
326
+ }
327
+ // Write data as-is (don't split - data is already properly formatted)
328
+ res.write(`data: ${data.data}\n`);
329
+ res.write('\n');
330
+ console.log(`[Tunnel Server] Wrote ${data.data.length} bytes of data`);
331
+ // Check if this is a "close" event
332
+ if (data.event === 'close') {
333
+ console.log(`[Tunnel Server] Close event received, ending SSE connection ${data.id}`);
334
+ // Clear keepalive interval if it exists
335
+ if (connection.keepaliveInterval) {
336
+ clearInterval(connection.keepaliveInterval);
337
+ console.log(`[Tunnel Server] Cleared keepalive interval for ${data.id}`);
338
+ }
339
+ res.end();
340
+ this.sseConnections.delete(data.id);
341
+ console.log(`[Tunnel Server] SSE connection closed. Remaining: ${this.sseConnections.size}`);
342
+ }
343
+ console.log(`[Tunnel Server] ====== SSE MESSAGE HANDLED ======`);
311
344
  }
312
- // Write data as-is (don't split - data is already properly formatted)
313
- res.write(`data: ${data.data}\n`);
314
- res.write('\n');
315
- // Check if this is a "close" event
316
- if (data.event === 'close') {
317
- res.end();
345
+ catch (error) {
346
+ console.error(`[Tunnel Server] Error writing SSE message for ${data.id}:`, error);
347
+ // Clear keepalive interval if it exists
348
+ if (connection === null || connection === void 0 ? void 0 : connection.keepaliveInterval) {
349
+ clearInterval(connection.keepaliveInterval);
350
+ }
318
351
  this.sseConnections.delete(data.id);
319
352
  }
320
353
  }
@@ -346,9 +379,14 @@ class DainTunnelServer {
346
379
  }
347
380
  async handleRequest(req, res) {
348
381
  const tunnelId = req.params.tunnelId;
382
+ // Log incoming request with headers for debugging
383
+ console.log(`[Tunnel Server] Incoming request: ${req.method} ${req.url} to tunnel ${tunnelId}`);
384
+ console.log(`[Tunnel Server] Accept header: "${req.headers.accept}"`);
385
+ console.log(`[Tunnel Server] Content-Type: "${req.headers['content-type']}"`);
349
386
  // Check for upgraded connections (WebSockets) - these are handled by the WebSocket server
350
387
  if (req.headers.upgrade && req.headers.upgrade.toLowerCase() === 'websocket') {
351
388
  // This is handled by the WebSocket server now
389
+ console.log(`[Tunnel Server] WebSocket upgrade detected, skipping`);
352
390
  return;
353
391
  }
354
392
  let tunnel;
@@ -366,8 +404,12 @@ class DainTunnelServer {
366
404
  console.log(`Tunnel not found after retries: ${tunnelId}`);
367
405
  return res.status(404).send("Tunnel not found");
368
406
  }
369
- // Check for SSE request
370
- if (req.headers.accept && req.headers.accept.includes('text/event-stream')) {
407
+ // Check for SSE request - more robust check
408
+ const acceptHeader = (req.headers.accept || '').toLowerCase();
409
+ const isSSE = acceptHeader.includes('text/event-stream');
410
+ console.log(`[Tunnel Server] Is SSE request: ${isSSE} (accept: "${acceptHeader}")`);
411
+ if (isSSE) {
412
+ console.log(`[Tunnel Server] Routing to SSE handler`);
371
413
  return this.handleSSERequest(req, res, tunnelId, tunnel);
372
414
  }
373
415
  // Handle regular HTTP request
@@ -390,37 +432,118 @@ class DainTunnelServer {
390
432
  handleSSERequest(req, res, tunnelId, tunnel) {
391
433
  // Setup SSE connection
392
434
  const sseId = (0, uuid_1.v4)();
393
- res.writeHead(200, {
394
- 'Content-Type': 'text/event-stream',
395
- 'Cache-Control': 'no-cache',
396
- 'Connection': 'keep-alive'
397
- });
398
- // Send initial connection event
399
- res.write('\n');
400
- // Store the SSE connection
401
- this.sseConnections.set(sseId, {
402
- req,
403
- res,
404
- id: sseId,
405
- tunnelId
406
- });
407
- // Notify the tunnel client about the new SSE connection
408
- tunnel.ws.send(JSON.stringify({
409
- type: "sse_connection",
410
- id: sseId,
411
- path: req.url,
412
- method: req.method, // Include HTTP method for POST tool calls
413
- headers: req.headers,
414
- body: req.method !== "GET" && req.body ? req.body.toString("base64") : undefined
415
- }));
416
- // Handle client disconnect
417
- req.on('close', () => {
418
- tunnel.ws.send(JSON.stringify({
419
- type: "sse_close",
420
- id: sseId
421
- }));
435
+ console.log(`[Tunnel Server] ====== SSE CONNECTION SETUP START ======`);
436
+ console.log(`[Tunnel Server] SSE ID: ${sseId}`);
437
+ console.log(`[Tunnel Server] Tunnel ID: ${tunnelId}`);
438
+ console.log(`[Tunnel Server] Path: ${req.url}`);
439
+ console.log(`[Tunnel Server] Method: ${req.method}`);
440
+ try {
441
+ // CRITICAL: Disable TCP buffering (Nagle's algorithm) to ensure immediate data sending
442
+ // Without this, the initial keepalive may be buffered and not reach the browser,
443
+ // causing it to close the connection immediately
444
+ if (req.socket) {
445
+ req.socket.setNoDelay(true);
446
+ req.socket.setTimeout(0);
447
+ console.log(`[Tunnel Server] Socket configured: noDelay=true, timeout=0 for ${sseId}`);
448
+ }
449
+ res.writeHead(200, {
450
+ 'Content-Type': 'text/event-stream',
451
+ 'Cache-Control': 'no-cache',
452
+ 'Connection': 'keep-alive',
453
+ 'X-Accel-Buffering': 'no', // Disable nginx buffering
454
+ 'Content-Encoding': 'identity', // Prevent compression middleware from buffering
455
+ 'Transfer-Encoding': 'chunked' // Ensure chunked encoding for streaming
456
+ });
457
+ console.log(`[Tunnel Server] SSE headers written for ${sseId}`);
458
+ // Send initial connection event - MUST be sent immediately to prevent browser timeout
459
+ res.write(': keepalive\n\n');
460
+ // Force flush if available (some environments support this)
461
+ if (typeof res.flush === 'function') {
462
+ res.flush();
463
+ console.log(`[Tunnel Server] Response flushed for ${sseId}`);
464
+ }
465
+ console.log(`[Tunnel Server] Initial keepalive sent for ${sseId}`);
466
+ // Send keepalive comments every 5 seconds to prevent timeout
467
+ const keepaliveInterval = setInterval(() => {
468
+ if (this.sseConnections.has(sseId)) {
469
+ try {
470
+ res.write(': keepalive\n\n');
471
+ console.log(`[Tunnel Server] Keepalive sent for ${sseId}`);
472
+ }
473
+ catch (error) {
474
+ console.error(`[Tunnel Server] Error sending keepalive for ${sseId}:`, error);
475
+ clearInterval(keepaliveInterval);
476
+ this.sseConnections.delete(sseId);
477
+ }
478
+ }
479
+ else {
480
+ clearInterval(keepaliveInterval);
481
+ }
482
+ }, 5000);
483
+ // Store the SSE connection BEFORE notifying tunnel client
484
+ this.sseConnections.set(sseId, {
485
+ req,
486
+ res,
487
+ id: sseId,
488
+ tunnelId,
489
+ keepaliveInterval
490
+ });
491
+ console.log(`[Tunnel Server] SSE connection stored. Total connections: ${this.sseConnections.size}`);
492
+ console.log(`[Tunnel Server] Active SSE IDs: ${Array.from(this.sseConnections.keys()).join(', ')}`);
493
+ // Notify the tunnel client about the new SSE connection
494
+ const sseConnectionMessage = {
495
+ type: "sse_connection",
496
+ id: sseId,
497
+ path: req.url,
498
+ method: req.method, // Include HTTP method for POST tool calls
499
+ headers: req.headers,
500
+ body: req.method !== "GET" && req.body ? req.body.toString("base64") : undefined
501
+ };
502
+ console.log(`[Tunnel Server] Sending sse_connection message to tunnel client`);
503
+ try {
504
+ tunnel.ws.send(JSON.stringify(sseConnectionMessage));
505
+ console.log(`[Tunnel Server] sse_connection message sent successfully for ${sseId}`);
506
+ }
507
+ catch (sendError) {
508
+ console.error(`[Tunnel Server] FAILED to send sse_connection message for ${sseId}:`, sendError);
509
+ // Clean up the SSE connection if we can't notify the client
510
+ this.sseConnections.delete(sseId);
511
+ res.status(500).end();
512
+ return;
513
+ }
514
+ // Handle client disconnect
515
+ req.on('close', () => {
516
+ console.log(`[Tunnel Server] SSE client disconnected: ${sseId}`);
517
+ console.log(`[Tunnel Server] Connection was active for tunnel: ${tunnelId}`);
518
+ clearInterval(keepaliveInterval);
519
+ try {
520
+ tunnel.ws.send(JSON.stringify({
521
+ type: "sse_close",
522
+ id: sseId
523
+ }));
524
+ console.log(`[Tunnel Server] Sent sse_close message for ${sseId}`);
525
+ }
526
+ catch (closeError) {
527
+ console.error(`[Tunnel Server] Error sending sse_close message:`, closeError);
528
+ }
529
+ this.sseConnections.delete(sseId);
530
+ console.log(`[Tunnel Server] SSE connection removed. Remaining: ${this.sseConnections.size}`);
531
+ });
532
+ // Add error handler for the response
533
+ res.on('error', (error) => {
534
+ console.error(`[Tunnel Server] SSE response error for ${sseId}:`, error);
535
+ clearInterval(keepaliveInterval);
536
+ this.sseConnections.delete(sseId);
537
+ });
538
+ console.log(`[Tunnel Server] ====== SSE CONNECTION SETUP COMPLETE ======`);
539
+ }
540
+ catch (error) {
541
+ console.error(`[Tunnel Server] CRITICAL ERROR in handleSSERequest for ${sseId}:`, error);
422
542
  this.sseConnections.delete(sseId);
423
- });
543
+ if (!res.headersSent) {
544
+ res.status(500).send('SSE setup failed');
545
+ }
546
+ }
424
547
  }
425
548
  removeTunnel(ws) {
426
549
  try {
package/package.json CHANGED
@@ -1,17 +1,25 @@
1
1
  {
2
2
  "name": "@dainprotocol/tunnel",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "build:types": "tsc --emitDeclarationOnly",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch",
15
+ "prepublishOnly": "npm run build && npm run build:types",
16
+ "start-server": "ts-node src/server/start.ts"
17
+ },
10
18
  "keywords": [],
11
19
  "author": "Ryan",
12
20
  "license": "ISC",
13
21
  "dependencies": {
14
- "@dainprotocol/service-sdk": "^1.3.6",
22
+ "@dainprotocol/service-sdk": "^2.0.31",
15
23
  "@types/body-parser": "^1.19.5",
16
24
  "@types/cors": "^2.8.17",
17
25
  "@types/eventsource": "^3.0.0",
@@ -68,12 +76,5 @@
68
76
  "./dist/server/*.d.ts"
69
77
  ]
70
78
  }
71
- },
72
- "scripts": {
73
- "build": "tsc",
74
- "build:types": "tsc --emitDeclarationOnly",
75
- "test": "jest",
76
- "test:watch": "jest --watch",
77
- "start-server": "ts-node src/server/start.ts"
78
79
  }
79
- }
80
+ }