@dolusoft/claude-collab 1.8.4 → 1.8.6
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.js +133 -56
- package/dist/cli.js.map +1 -1
- package/dist/mcp-main.js +133 -56
- package/dist/mcp-main.js.map +1 -1
- package/package.json +1 -1
package/dist/mcp-main.js
CHANGED
|
@@ -6,7 +6,7 @@ import { execFile, spawn } from 'child_process';
|
|
|
6
6
|
import { unlinkSync } from 'fs';
|
|
7
7
|
import { tmpdir } from 'os';
|
|
8
8
|
import { join } from 'path';
|
|
9
|
-
import {
|
|
9
|
+
import { createSocket } from 'dgram';
|
|
10
10
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
11
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
12
12
|
import { z } from 'zod';
|
|
@@ -666,28 +666,48 @@ var HubServer = class {
|
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
};
|
|
669
|
-
var
|
|
670
|
-
var
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
669
|
+
var DISCOVERY_PORT = 9998;
|
|
670
|
+
var BROADCAST_INTERVAL_MS = 3e3;
|
|
671
|
+
var BROADCAST_ADDRESS = "255.255.255.255";
|
|
672
|
+
var HubBroadcaster = class {
|
|
673
|
+
socket = null;
|
|
674
|
+
timer = null;
|
|
675
|
+
start(hubPort) {
|
|
676
|
+
if (this.socket) return;
|
|
677
|
+
const socket = createSocket("udp4");
|
|
678
|
+
this.socket = socket;
|
|
679
|
+
socket.on("error", (err) => {
|
|
680
|
+
console.error("[hub-broadcaster] error:", err.message);
|
|
681
|
+
});
|
|
682
|
+
socket.bind(0, () => {
|
|
683
|
+
socket.setBroadcast(true);
|
|
684
|
+
const send = () => {
|
|
685
|
+
if (!this.socket) return;
|
|
686
|
+
const msg = Buffer.from(JSON.stringify({ type: "claude-collab-hub", port: hubPort }));
|
|
687
|
+
socket.send(msg, 0, msg.length, DISCOVERY_PORT, BROADCAST_ADDRESS, (err) => {
|
|
688
|
+
if (err) console.error("[hub-broadcaster] send error:", err.message);
|
|
689
|
+
});
|
|
690
|
+
};
|
|
691
|
+
send();
|
|
692
|
+
this.timer = setInterval(send, BROADCAST_INTERVAL_MS);
|
|
693
|
+
});
|
|
676
694
|
}
|
|
677
695
|
stop() {
|
|
678
|
-
if (
|
|
679
|
-
|
|
680
|
-
this.
|
|
681
|
-
}
|
|
682
|
-
this.
|
|
683
|
-
|
|
696
|
+
if (this.timer) {
|
|
697
|
+
clearInterval(this.timer);
|
|
698
|
+
this.timer = null;
|
|
699
|
+
}
|
|
700
|
+
if (this.socket) {
|
|
701
|
+
this.socket.close();
|
|
702
|
+
this.socket = null;
|
|
703
|
+
}
|
|
684
704
|
}
|
|
685
705
|
};
|
|
686
706
|
|
|
687
707
|
// src/infrastructure/hub/hub-manager.ts
|
|
688
708
|
var HubManager = class {
|
|
689
709
|
hubServer = null;
|
|
690
|
-
|
|
710
|
+
broadcaster = null;
|
|
691
711
|
currentPort = null;
|
|
692
712
|
get isRunning() {
|
|
693
713
|
return this.hubServer !== null;
|
|
@@ -701,9 +721,9 @@ var HubManager = class {
|
|
|
701
721
|
server.start(port);
|
|
702
722
|
this.hubServer = server;
|
|
703
723
|
this.currentPort = port;
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
this.
|
|
724
|
+
const broadcaster = new HubBroadcaster();
|
|
725
|
+
broadcaster.start(port);
|
|
726
|
+
this.broadcaster = broadcaster;
|
|
707
727
|
let firewallAdded = false;
|
|
708
728
|
try {
|
|
709
729
|
await addFirewallRule(port);
|
|
@@ -715,9 +735,9 @@ var HubManager = class {
|
|
|
715
735
|
}
|
|
716
736
|
async stop() {
|
|
717
737
|
if (!this.isRunning) throw new Error("Hub is not running");
|
|
718
|
-
if (this.
|
|
719
|
-
this.
|
|
720
|
-
this.
|
|
738
|
+
if (this.broadcaster) {
|
|
739
|
+
this.broadcaster.stop();
|
|
740
|
+
this.broadcaster = null;
|
|
721
741
|
}
|
|
722
742
|
const port = this.currentPort;
|
|
723
743
|
this.hubServer.stop();
|
|
@@ -1012,6 +1032,97 @@ function registerStopHubTool(server, hubManager) {
|
|
|
1012
1032
|
}
|
|
1013
1033
|
);
|
|
1014
1034
|
}
|
|
1035
|
+
function discoverHub(timeoutMs = 1e4) {
|
|
1036
|
+
return new Promise((resolve) => {
|
|
1037
|
+
const socket = createSocket("udp4");
|
|
1038
|
+
let settled = false;
|
|
1039
|
+
const finish = (result) => {
|
|
1040
|
+
if (settled) return;
|
|
1041
|
+
settled = true;
|
|
1042
|
+
clearTimeout(timer);
|
|
1043
|
+
try {
|
|
1044
|
+
socket.close();
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
resolve(result);
|
|
1048
|
+
};
|
|
1049
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
1050
|
+
socket.on("error", () => finish(null));
|
|
1051
|
+
socket.on("message", (msg, rinfo) => {
|
|
1052
|
+
try {
|
|
1053
|
+
const data = JSON.parse(msg.toString());
|
|
1054
|
+
if (data.type === "claude-collab-hub" && typeof data.port === "number") {
|
|
1055
|
+
finish({ host: rinfo.address, port: data.port });
|
|
1056
|
+
}
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
socket.bind(DISCOVERY_PORT, "0.0.0.0");
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
function watchForHub(onFound) {
|
|
1064
|
+
const socket = createSocket("udp4");
|
|
1065
|
+
socket.on("error", (err) => {
|
|
1066
|
+
console.error("[hub-listener] error:", err.message);
|
|
1067
|
+
});
|
|
1068
|
+
socket.on("message", (msg, rinfo) => {
|
|
1069
|
+
try {
|
|
1070
|
+
const data = JSON.parse(msg.toString());
|
|
1071
|
+
if (data.type === "claude-collab-hub" && typeof data.port === "number") {
|
|
1072
|
+
onFound({ host: rinfo.address, port: data.port });
|
|
1073
|
+
}
|
|
1074
|
+
} catch {
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
socket.bind(DISCOVERY_PORT, "0.0.0.0");
|
|
1078
|
+
return () => {
|
|
1079
|
+
try {
|
|
1080
|
+
socket.close();
|
|
1081
|
+
} catch {
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// src/presentation/mcp/tools/connect.tool.ts
|
|
1087
|
+
function registerConnectTool(server, client) {
|
|
1088
|
+
server.tool(
|
|
1089
|
+
"connect",
|
|
1090
|
+
"Find and connect to the hub on the LAN automatically. No IP or port needed.",
|
|
1091
|
+
{},
|
|
1092
|
+
async () => {
|
|
1093
|
+
if (client.isConnected) {
|
|
1094
|
+
const info = client.getInfo();
|
|
1095
|
+
return {
|
|
1096
|
+
content: [{ type: "text", text: `Already connected to hub as "${info.teamName}".` }]
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
const hub = await discoverHub(1e4);
|
|
1100
|
+
if (!hub) {
|
|
1101
|
+
return {
|
|
1102
|
+
content: [{
|
|
1103
|
+
type: "text",
|
|
1104
|
+
text: "No hub found on the LAN. Make sure someone has called start_hub on the host machine."
|
|
1105
|
+
}]
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
try {
|
|
1109
|
+
await client.connectToHub(`ws://${hub.host}:${hub.port}`);
|
|
1110
|
+
const info = client.getInfo();
|
|
1111
|
+
return {
|
|
1112
|
+
content: [{
|
|
1113
|
+
type: "text",
|
|
1114
|
+
text: `Connected to hub at ${hub.host}:${hub.port} as "${info.teamName}".`
|
|
1115
|
+
}]
|
|
1116
|
+
};
|
|
1117
|
+
} catch (err) {
|
|
1118
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1119
|
+
return {
|
|
1120
|
+
content: [{ type: "text", text: `Hub found at ${hub.host}:${hub.port} but connection failed: ${msg}` }]
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1015
1126
|
|
|
1016
1127
|
// src/presentation/mcp/server.ts
|
|
1017
1128
|
function createMcpServer(options) {
|
|
@@ -1026,6 +1137,7 @@ function createMcpServer(options) {
|
|
|
1026
1137
|
registerHistoryTool(server, client);
|
|
1027
1138
|
registerStartHubTool(server, client, hubManager);
|
|
1028
1139
|
registerStopHubTool(server, hubManager);
|
|
1140
|
+
registerConnectTool(server, client);
|
|
1029
1141
|
return server;
|
|
1030
1142
|
}
|
|
1031
1143
|
async function startMcpServer(options) {
|
|
@@ -1033,41 +1145,6 @@ async function startMcpServer(options) {
|
|
|
1033
1145
|
const transport = new StdioServerTransport();
|
|
1034
1146
|
await server.connect(transport);
|
|
1035
1147
|
}
|
|
1036
|
-
var SERVICE_TYPE2 = "claude-collab";
|
|
1037
|
-
function resolveHost(svc) {
|
|
1038
|
-
const ipv4 = svc.addresses?.find((a) => /^\d+\.\d+\.\d+\.\d+$/.test(a));
|
|
1039
|
-
return ipv4 ?? svc.host;
|
|
1040
|
-
}
|
|
1041
|
-
function discoverHub(timeoutMs = 5e3) {
|
|
1042
|
-
return new Promise((resolve) => {
|
|
1043
|
-
const bonjour = new Bonjour();
|
|
1044
|
-
const browser = bonjour.find({ type: SERVICE_TYPE2 });
|
|
1045
|
-
let settled = false;
|
|
1046
|
-
const finish = (result) => {
|
|
1047
|
-
if (settled) return;
|
|
1048
|
-
settled = true;
|
|
1049
|
-
clearTimeout(timer);
|
|
1050
|
-
browser.stop();
|
|
1051
|
-
bonjour.destroy();
|
|
1052
|
-
resolve(result);
|
|
1053
|
-
};
|
|
1054
|
-
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
1055
|
-
browser.on("up", (svc) => {
|
|
1056
|
-
finish({ host: resolveHost(svc), port: svc.port });
|
|
1057
|
-
});
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
function watchForHub(onFound) {
|
|
1061
|
-
const bonjour = new Bonjour();
|
|
1062
|
-
const browser = bonjour.find({ type: SERVICE_TYPE2 });
|
|
1063
|
-
browser.on("up", (svc) => {
|
|
1064
|
-
onFound({ host: resolveHost(svc), port: svc.port });
|
|
1065
|
-
});
|
|
1066
|
-
return () => {
|
|
1067
|
-
browser.stop();
|
|
1068
|
-
bonjour.destroy();
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
1148
|
|
|
1072
1149
|
// src/mcp-main.ts
|
|
1073
1150
|
function getArg(flag) {
|