@dolusoft/claude-collab 1.11.0 → 1.11.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/dist/mcp-main.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { WebSocket, WebSocketServer } from 'ws';
3
3
  import { v4 } from 'uuid';
4
4
  import os, { tmpdir } from 'os';
5
- import { execFile, spawn } from 'child_process';
5
+ import { execSync, execFile, spawn } from 'child_process';
6
6
  import { EventEmitter } from 'events';
7
7
  import { unlinkSync } from 'fs';
8
8
  import { join } from 'path';
@@ -10,7 +10,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
10
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
11
  import { z } from 'zod';
12
12
 
13
- // src/infrastructure/p2p/p2p-protocol.ts
13
+ // src/infrastructure/mesh/protocol.ts
14
14
  function serialize(msg) {
15
15
  return JSON.stringify(msg);
16
16
  }
@@ -262,9 +262,9 @@ var InjectionQueue = class extends EventEmitter {
262
262
  };
263
263
  var injectionQueue = new InjectionQueue();
264
264
 
265
- // src/infrastructure/p2p/p2p-node.ts
265
+ // src/infrastructure/mesh/mesh-node.ts
266
266
  var FIXED_PORT = 12345;
267
- var P2PNode = class {
267
+ var MeshNode = class {
268
268
  server = null;
269
269
  myName = "";
270
270
  running = false;
@@ -351,7 +351,7 @@ var P2PNode = class {
351
351
  this.wsToName.delete(ws);
352
352
  if (this.peerConnections.get(name) === ws) {
353
353
  this.peerConnections.delete(name);
354
- console.error(`[p2p] disconnected from peer: ${name}`);
354
+ console.error(`[mesh] disconnected from peer: ${name}`);
355
355
  }
356
356
  }
357
357
  });
@@ -365,7 +365,7 @@ var P2PNode = class {
365
365
  async ask(toPeer, content, format) {
366
366
  const ws = this.peerConnections.get(toPeer);
367
367
  if (!ws || ws.readyState !== WebSocket.OPEN) {
368
- throw new Error(`Peer "${toPeer}" is not connected. Use peers() to see who's online.`);
368
+ throw new Error(`Peer "${toPeer}" is not connected. Use status() to see who's online.`);
369
369
  }
370
370
  const questionId = v4();
371
371
  this.sentQuestions.set(questionId, { toPeer, content, askedAt: (/* @__PURE__ */ new Date()).toISOString() });
@@ -425,7 +425,7 @@ var P2PNode = class {
425
425
  this.sendToWs(ws, answerMsg);
426
426
  } else {
427
427
  this.pendingOutboundAnswers.set(senderName, answerMsg);
428
- console.error(`[p2p] "${senderName}" is offline, answer queued for delivery on reconnect`);
428
+ console.error(`[mesh] "${senderName}" is offline, answer queued for delivery on reconnect`);
429
429
  }
430
430
  }
431
431
  injectionQueue.notifyReplied();
@@ -499,13 +499,27 @@ var P2PNode = class {
499
499
  // ---------------------------------------------------------------------------
500
500
  // Private: server startup
501
501
  // ---------------------------------------------------------------------------
502
- startServer() {
502
+ async startServer() {
503
+ try {
504
+ await this.tryBind();
505
+ } catch (err) {
506
+ const nodeErr = err;
507
+ if (nodeErr.code === "EADDRINUSE") {
508
+ console.error(`[mesh] port ${FIXED_PORT} in use \u2014 killing existing process`);
509
+ await killProcessOnPort(FIXED_PORT);
510
+ await this.tryBind();
511
+ } else {
512
+ throw err;
513
+ }
514
+ }
515
+ }
516
+ tryBind() {
503
517
  return new Promise((resolve, reject) => {
504
518
  const wss = new WebSocketServer({ port: FIXED_PORT });
505
519
  wss.once("listening", () => {
506
520
  this.server = wss;
507
521
  this.running = true;
508
- console.error(`[p2p] listening on port ${FIXED_PORT} as "${this.myName}"`);
522
+ console.error(`[mesh] listening on port ${FIXED_PORT} as "${this.myName}"`);
509
523
  this.attachServerHandlers(wss);
510
524
  resolve();
511
525
  });
@@ -531,16 +545,16 @@ var P2PNode = class {
531
545
  this.wsToName.delete(ws);
532
546
  if (this.peerConnections.get(name) === ws) {
533
547
  this.peerConnections.delete(name);
534
- console.error(`[p2p] peer disconnected (inbound): ${name}`);
548
+ console.error(`[mesh] peer disconnected (inbound): ${name}`);
535
549
  }
536
550
  }
537
551
  });
538
552
  ws.on("error", (err) => {
539
- console.error("[p2p] inbound ws error:", err.message);
553
+ console.error("[mesh] inbound ws error:", err.message);
540
554
  });
541
555
  });
542
556
  wss.on("error", (err) => {
543
- console.error("[p2p] server error:", err.message);
557
+ console.error("[mesh] server error:", err.message);
544
558
  });
545
559
  }
546
560
  // ---------------------------------------------------------------------------
@@ -564,7 +578,7 @@ var P2PNode = class {
564
578
  this.wsToName.set(ws, name);
565
579
  this.peerIPs.set(name, ip);
566
580
  this.connectingPeers.delete(name);
567
- console.error(`[p2p] peer registered: ${name} @ ${ip}`);
581
+ console.error(`[mesh] peer registered: ${name} @ ${ip}`);
568
582
  this.deliverPendingAnswer(name, ws);
569
583
  }
570
584
  /** Connect outbound to a peer discovered via PEER_LIST or PEER_ANNOUNCE. */
@@ -589,12 +603,12 @@ var P2PNode = class {
589
603
  this.wsToName.delete(ws);
590
604
  if (this.peerConnections.get(peerName) === ws) {
591
605
  this.peerConnections.delete(peerName);
592
- console.error(`[p2p] disconnected from mesh peer: ${peerName}`);
606
+ console.error(`[mesh] disconnected from mesh peer: ${peerName}`);
593
607
  }
594
608
  }
595
609
  });
596
610
  ws.on("error", (err) => {
597
- console.error(`[p2p] mesh connect to "${name}" @ ${ip} failed: ${err.message}`);
611
+ console.error(`[mesh] connect to "${name}" @ ${ip} failed: ${err.message}`);
598
612
  this.connectingPeers.delete(name);
599
613
  });
600
614
  }
@@ -611,7 +625,7 @@ var P2PNode = class {
611
625
  }
612
626
  this.registerPeer(msg.name, remoteIp, ws);
613
627
  this.sendToWs(ws, { type: "HELLO_ACK", name: this.myName });
614
- console.error(`[p2p] peer joined (inbound): ${msg.name}`);
628
+ console.error(`[mesh] peer joined (inbound): ${msg.name}`);
615
629
  this.afterHandshake(msg.name, ws);
616
630
  return;
617
631
  }
@@ -626,7 +640,7 @@ var P2PNode = class {
626
640
  this.peerConnections.set(msg.name, ws);
627
641
  this.wsToName.set(ws, msg.name);
628
642
  this.connectingPeers.delete(msg.name);
629
- console.error(`[p2p] connected to mesh peer: ${msg.name}`);
643
+ console.error(`[mesh] connected to mesh peer: ${msg.name}`);
630
644
  this.deliverPendingAnswer(msg.name, ws);
631
645
  this.afterHandshake(msg.name, ws);
632
646
  }
@@ -634,7 +648,7 @@ var P2PNode = class {
634
648
  case "PEER_LIST":
635
649
  for (const peer of msg.peers) {
636
650
  if (peer.name !== this.myName && !this.peerConnections.has(peer.name)) {
637
- console.error(`[p2p] mesh: connecting to ${peer.name} @ ${peer.ip} via PEER_LIST`);
651
+ console.error(`[mesh] connecting to ${peer.name} @ ${peer.ip} via PEER_LIST`);
638
652
  this.peerIPs.set(peer.name, peer.ip);
639
653
  this.connectMeshPeer(peer.name, peer.ip);
640
654
  }
@@ -642,7 +656,7 @@ var P2PNode = class {
642
656
  break;
643
657
  case "PEER_ANNOUNCE":
644
658
  if (msg.name !== this.myName && !this.peerConnections.has(msg.name)) {
645
- console.error(`[p2p] mesh: connecting to ${msg.name} @ ${msg.ip} via PEER_ANNOUNCE`);
659
+ console.error(`[mesh] connecting to ${msg.name} @ ${msg.ip} via PEER_ANNOUNCE`);
646
660
  this.peerIPs.set(msg.name, msg.ip);
647
661
  this.connectMeshPeer(msg.name, msg.ip);
648
662
  }
@@ -694,7 +708,7 @@ var P2PNode = class {
694
708
  if (pending) {
695
709
  this.pendingOutboundAnswers.delete(peerName);
696
710
  this.sendToWs(ws, pending);
697
- console.error(`[p2p] delivered queued answer to "${peerName}" after reconnect`);
711
+ console.error(`[mesh] delivered queued answer to "${peerName}" after reconnect`);
698
712
  }
699
713
  }
700
714
  sendToWs(ws, msg) {
@@ -719,6 +733,34 @@ var P2PNode = class {
719
733
  });
720
734
  }
721
735
  };
736
+ async function killProcessOnPort(port) {
737
+ try {
738
+ if (process.platform === "win32") {
739
+ const out = execSync(
740
+ `netstat -ano | findstr ":${port} "`,
741
+ { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
742
+ );
743
+ const pids = /* @__PURE__ */ new Set();
744
+ for (const line of out.split("\n")) {
745
+ const parts = line.trim().split(/\s+/);
746
+ if (parts.length >= 5 && parts[3] === "LISTENING" && parts[4]) {
747
+ pids.add(parts[4]);
748
+ }
749
+ }
750
+ for (const pid of pids) {
751
+ try {
752
+ execSync(`taskkill /PID ${pid} /F`, { stdio: "ignore" });
753
+ console.error(`[mesh] killed PID ${pid} on port ${port}`);
754
+ } catch {
755
+ }
756
+ }
757
+ } else {
758
+ execSync(`fuser -k ${port}/tcp`, { stdio: "ignore" });
759
+ }
760
+ } catch {
761
+ }
762
+ await new Promise((resolve) => setTimeout(resolve, 300));
763
+ }
722
764
  var CONNECT_DESCRIPTION = `Connect to another Claude instance by their IP address.
723
765
 
724
766
  WHEN TO USE:
@@ -821,7 +863,7 @@ function registerAskTool(server, client) {
821
863
  return {
822
864
  content: [{
823
865
  type: "text",
824
- text: "P2P node is not ready yet. Wait a moment and try again."
866
+ text: "Node is not ready yet. Wait a moment and try again."
825
867
  }],
826
868
  isError: true
827
869
  };
@@ -902,7 +944,7 @@ function registerReplyTool(server, client) {
902
944
  return {
903
945
  content: [{
904
946
  type: "text",
905
- text: "P2P node is not ready yet. Wait a moment and try again."
947
+ text: "Node is not ready yet. Wait a moment and try again."
906
948
  }],
907
949
  isError: true
908
950
  };
@@ -929,68 +971,6 @@ function registerReplyTool(server, client) {
929
971
  });
930
972
  }
931
973
 
932
- // src/presentation/mcp/tools/peers.tool.ts
933
- var PEERS_DESCRIPTION = `List all currently active peer connections.
934
-
935
- WHEN TO USE:
936
- - Before calling ask() \u2014 to confirm the target peer is online and get their exact name
937
- - After connect() \u2014 to verify the connection succeeded and the mesh formed
938
- - When a peer seems unreachable \u2014 to check if they are still connected
939
-
940
- WHAT IT SHOWS:
941
- - Your own name and port
942
- - All peers with an active direct connection
943
-
944
- IF NO PEERS ARE LISTED:
945
- - Use connect(ip) to connect to a peer
946
- - Ask your teammate to run status() to get their IP`;
947
- function registerPeersTool(server, client) {
948
- server.tool("peers", PEERS_DESCRIPTION, {}, async () => {
949
- const info = client.getInfo();
950
- const myName = info.teamName ?? "(starting...)";
951
- const myPort = info.port ?? "?";
952
- const connected = info.connectedPeers;
953
- if (!client.isConnected) {
954
- return {
955
- content: [{
956
- type: "text",
957
- text: [
958
- `P2P server is not running yet (port ${myPort} may be in use).`,
959
- `Check the MCP process logs for the error details.`
960
- ].join("\n")
961
- }],
962
- isError: true
963
- };
964
- }
965
- if (connected.length === 0) {
966
- return {
967
- content: [{
968
- type: "text",
969
- text: [
970
- `You are "${myName}" (listening on port ${myPort}).`,
971
- `No peers connected yet.`,
972
- ``,
973
- `Use connect(ip) to connect to a peer.`,
974
- `Ask your teammate to run status() to get their IP.`
975
- ].join("\n")
976
- }]
977
- };
978
- }
979
- const peerIPs = info.peerIPs ?? {};
980
- const list = connected.map((name) => {
981
- const ip = peerIPs[name] ? ` (${peerIPs[name]})` : "";
982
- return ` \u2022 ${name}${ip}`;
983
- }).join("\n");
984
- return {
985
- content: [{
986
- type: "text",
987
- text: `You are "${myName}" (port ${myPort}). Connected peers (${connected.length}):
988
- ${list}`
989
- }]
990
- };
991
- });
992
- }
993
-
994
974
  // src/presentation/mcp/tools/history.tool.ts
995
975
  var HISTORY_DESCRIPTION = `Show all questions and answers exchanged this session \u2014 both sent and received.
996
976
 
@@ -1059,12 +1039,23 @@ function registerStatusTool(server, client) {
1059
1039
  };
1060
1040
  }
1061
1041
  let ips = [];
1062
- if (client instanceof P2PNode) {
1042
+ if (client instanceof MeshNode) {
1063
1043
  ips = client.getLocalIps();
1064
1044
  }
1065
1045
  const ipLine = ips.length > 0 ? ips.join(", ") : "(could not detect \u2014 check your network interface)";
1066
- const peerCount = info.connectedPeers.length;
1067
- const peerLine = peerCount === 0 ? "No peers connected yet. Share your IP so others can connect(ip)." : `Connected to ${peerCount} peer(s): ${info.connectedPeers.map((n) => `"${n}"`).join(", ")}`;
1046
+ const peerIPs = info.peerIPs ?? {};
1047
+ const connected = info.connectedPeers;
1048
+ let peersSection;
1049
+ if (connected.length === 0) {
1050
+ peersSection = "No peers connected yet. Share your IP so others can connect(ip).";
1051
+ } else {
1052
+ const list = connected.map((n) => {
1053
+ const ip = peerIPs[n] ? ` (${peerIPs[n]})` : "";
1054
+ return ` \u2022 ${n}${ip}`;
1055
+ }).join("\n");
1056
+ peersSection = `Connected peers (${connected.length}):
1057
+ ${list}`;
1058
+ }
1068
1059
  return {
1069
1060
  content: [{
1070
1061
  type: "text",
@@ -1073,7 +1064,7 @@ function registerStatusTool(server, client) {
1073
1064
  `IP: ${ipLine}`,
1074
1065
  `Port: ${port}`,
1075
1066
  ``,
1076
- peerLine
1067
+ peersSection
1077
1068
  ].join("\n")
1078
1069
  }]
1079
1070
  };
@@ -1091,7 +1082,6 @@ function createMcpServer(options) {
1091
1082
  registerStatusTool(server, client);
1092
1083
  registerAskTool(server, client);
1093
1084
  registerReplyTool(server, client);
1094
- registerPeersTool(server, client);
1095
1085
  registerHistoryTool(server, client);
1096
1086
  return server;
1097
1087
  }
@@ -1112,7 +1102,7 @@ async function main() {
1112
1102
  console.error("--name is required");
1113
1103
  process.exit(1);
1114
1104
  }
1115
- const node = new P2PNode();
1105
+ const node = new MeshNode();
1116
1106
  const mcpReady = startMcpServer({ client: node });
1117
1107
  node.join(name, name).catch((err) => {
1118
1108
  console.error(`[mcp-main] Failed to start on port 12345: ${err.message}`);