@dolusoft/claude-collab 1.11.2 → 1.11.3
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 +17 -88
- package/dist/cli.js.map +1 -1
- package/dist/mcp-main.js +17 -88
- package/dist/mcp-main.js.map +1 -1
- package/package.json +1 -1
package/dist/mcp-main.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
3
3
|
import { v4 } from 'uuid';
|
|
4
4
|
import os, { tmpdir } from 'os';
|
|
5
|
-
import {
|
|
5
|
+
import { exec, execFile, spawn } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
6
7
|
import { EventEmitter } from 'events';
|
|
7
8
|
import { unlinkSync } from 'fs';
|
|
8
9
|
import { join } from 'path';
|
|
@@ -263,6 +264,7 @@ var InjectionQueue = class extends EventEmitter {
|
|
|
263
264
|
var injectionQueue = new InjectionQueue();
|
|
264
265
|
|
|
265
266
|
// src/infrastructure/mesh/mesh-node.ts
|
|
267
|
+
var execAsync = promisify(exec);
|
|
266
268
|
var FIXED_PORT = 12345;
|
|
267
269
|
var MeshNode = class {
|
|
268
270
|
server = null;
|
|
@@ -279,12 +281,11 @@ var MeshNode = class {
|
|
|
279
281
|
connectingPeers = /* @__PURE__ */ new Set();
|
|
280
282
|
incomingQuestions = /* @__PURE__ */ new Map();
|
|
281
283
|
receivedAnswers = /* @__PURE__ */ new Map();
|
|
282
|
-
sentQuestions = /* @__PURE__ */ new Map();
|
|
283
284
|
questionToSender = /* @__PURE__ */ new Map();
|
|
284
285
|
pendingHandlers = /* @__PURE__ */ new Set();
|
|
285
286
|
// Push-based answer resolution: questionId → resolve callback
|
|
286
287
|
answerWaiters = /* @__PURE__ */ new Map();
|
|
287
|
-
// Answers queued for offline peers: peerName → AnswerMsg (delivered on reconnect)
|
|
288
|
+
// Answers queued for offline peers: peerName → AnswerMsg[] (delivered on reconnect)
|
|
288
289
|
pendingOutboundAnswers = /* @__PURE__ */ new Map();
|
|
289
290
|
// ---------------------------------------------------------------------------
|
|
290
291
|
// ICollabClient implementation
|
|
@@ -345,7 +346,6 @@ var MeshNode = class {
|
|
|
345
346
|
});
|
|
346
347
|
ws.on("close", () => {
|
|
347
348
|
clearTimeout(timeout);
|
|
348
|
-
this.connectingPeers.delete(ip);
|
|
349
349
|
const name = this.wsToName.get(ws);
|
|
350
350
|
if (name) {
|
|
351
351
|
this.wsToName.delete(ws);
|
|
@@ -357,7 +357,6 @@ var MeshNode = class {
|
|
|
357
357
|
});
|
|
358
358
|
ws.on("error", (err) => {
|
|
359
359
|
clearTimeout(timeout);
|
|
360
|
-
this.connectingPeers.delete(ip);
|
|
361
360
|
reject(new Error(`Failed to connect to ${ip}:${FIXED_PORT} \u2014 ${err.message}`));
|
|
362
361
|
});
|
|
363
362
|
});
|
|
@@ -368,7 +367,6 @@ var MeshNode = class {
|
|
|
368
367
|
throw new Error(`Peer "${toPeer}" is not connected. Use status() to see who's online.`);
|
|
369
368
|
}
|
|
370
369
|
const questionId = v4();
|
|
371
|
-
this.sentQuestions.set(questionId, { toPeer, content, askedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
372
370
|
const ackPromise = this.waitForResponse(
|
|
373
371
|
(m) => m.type === "ASK_ACK" && m.questionId === questionId,
|
|
374
372
|
5e3
|
|
@@ -411,6 +409,8 @@ var MeshNode = class {
|
|
|
411
409
|
question.answerContent = content;
|
|
412
410
|
question.answerFormat = format;
|
|
413
411
|
const senderName = this.questionToSender.get(questionId);
|
|
412
|
+
this.incomingQuestions.delete(questionId);
|
|
413
|
+
this.questionToSender.delete(questionId);
|
|
414
414
|
if (senderName) {
|
|
415
415
|
const answerMsg = {
|
|
416
416
|
type: "ANSWER",
|
|
@@ -424,7 +424,9 @@ var MeshNode = class {
|
|
|
424
424
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
425
425
|
this.sendToWs(ws, answerMsg);
|
|
426
426
|
} else {
|
|
427
|
-
this.pendingOutboundAnswers.
|
|
427
|
+
const queue = this.pendingOutboundAnswers.get(senderName) ?? [];
|
|
428
|
+
queue.push(answerMsg);
|
|
429
|
+
this.pendingOutboundAnswers.set(senderName, queue);
|
|
428
430
|
console.error(`[mesh] "${senderName}" is offline, answer queued for delivery on reconnect`);
|
|
429
431
|
}
|
|
430
432
|
}
|
|
@@ -432,7 +434,7 @@ var MeshNode = class {
|
|
|
432
434
|
}
|
|
433
435
|
async getInbox() {
|
|
434
436
|
const now = Date.now();
|
|
435
|
-
const questions = [...this.incomingQuestions.values()].
|
|
437
|
+
const questions = [...this.incomingQuestions.values()].map((q) => ({
|
|
436
438
|
questionId: q.questionId,
|
|
437
439
|
from: { displayName: `${q.fromName} Claude`, teamName: q.fromName },
|
|
438
440
|
content: q.content,
|
|
@@ -462,31 +464,6 @@ var MeshNode = class {
|
|
|
462
464
|
}
|
|
463
465
|
return result;
|
|
464
466
|
}
|
|
465
|
-
getHistory() {
|
|
466
|
-
const entries = [];
|
|
467
|
-
for (const [questionId, sent] of this.sentQuestions) {
|
|
468
|
-
const answer = this.receivedAnswers.get(questionId);
|
|
469
|
-
entries.push({
|
|
470
|
-
direction: "sent",
|
|
471
|
-
questionId,
|
|
472
|
-
peer: sent.toPeer,
|
|
473
|
-
question: sent.content,
|
|
474
|
-
askedAt: sent.askedAt,
|
|
475
|
-
...answer ? { answer: answer.content, answeredAt: answer.answeredAt } : {}
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
for (const [, incoming] of this.incomingQuestions) {
|
|
479
|
-
entries.push({
|
|
480
|
-
direction: "received",
|
|
481
|
-
questionId: incoming.questionId,
|
|
482
|
-
peer: incoming.fromName,
|
|
483
|
-
question: incoming.content,
|
|
484
|
-
askedAt: incoming.createdAt.toISOString(),
|
|
485
|
-
...incoming.answered && incoming.answerContent ? { answer: incoming.answerContent, answeredAt: (/* @__PURE__ */ new Date()).toISOString() } : {}
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
return entries.sort((a, b) => a.askedAt.localeCompare(b.askedAt));
|
|
489
|
-
}
|
|
490
467
|
async disconnect() {
|
|
491
468
|
for (const ws of this.peerConnections.values()) ws.close();
|
|
492
469
|
this.peerConnections.clear();
|
|
@@ -617,7 +594,6 @@ var MeshNode = class {
|
|
|
617
594
|
// ---------------------------------------------------------------------------
|
|
618
595
|
/** Handles messages on inbound connections (server side — we know the remote IP). */
|
|
619
596
|
handleInboundMessage(ws, remoteIp, msg) {
|
|
620
|
-
for (const handler of this.pendingHandlers) handler(msg);
|
|
621
597
|
if (msg.type === "HELLO") {
|
|
622
598
|
if (this.peerConnections.has(msg.name)) {
|
|
623
599
|
ws.terminate();
|
|
@@ -705,10 +681,10 @@ var MeshNode = class {
|
|
|
705
681
|
}
|
|
706
682
|
deliverPendingAnswer(peerName, ws) {
|
|
707
683
|
const pending = this.pendingOutboundAnswers.get(peerName);
|
|
708
|
-
if (pending) {
|
|
684
|
+
if (pending && pending.length > 0) {
|
|
709
685
|
this.pendingOutboundAnswers.delete(peerName);
|
|
710
|
-
this.sendToWs(ws,
|
|
711
|
-
console.error(`[mesh] delivered queued answer to "${peerName}" after reconnect`);
|
|
686
|
+
for (const msg of pending) this.sendToWs(ws, msg);
|
|
687
|
+
console.error(`[mesh] delivered ${pending.length} queued answer(s) to "${peerName}" after reconnect`);
|
|
712
688
|
}
|
|
713
689
|
}
|
|
714
690
|
sendToWs(ws, msg) {
|
|
@@ -736,12 +712,9 @@ var MeshNode = class {
|
|
|
736
712
|
async function killProcessOnPort(port) {
|
|
737
713
|
try {
|
|
738
714
|
if (process.platform === "win32") {
|
|
739
|
-
const
|
|
740
|
-
`netstat -ano | findstr ":${port} "`,
|
|
741
|
-
{ encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
|
|
742
|
-
);
|
|
715
|
+
const { stdout } = await execAsync(`netstat -ano | findstr ":${port} "`);
|
|
743
716
|
const pids = /* @__PURE__ */ new Set();
|
|
744
|
-
for (const line of
|
|
717
|
+
for (const line of stdout.split("\n")) {
|
|
745
718
|
const parts = line.trim().split(/\s+/);
|
|
746
719
|
if (parts.length >= 5 && parts[3] === "LISTENING" && parts[4]) {
|
|
747
720
|
pids.add(parts[4]);
|
|
@@ -749,13 +722,13 @@ async function killProcessOnPort(port) {
|
|
|
749
722
|
}
|
|
750
723
|
for (const pid of pids) {
|
|
751
724
|
try {
|
|
752
|
-
|
|
725
|
+
await execAsync(`taskkill /PID ${pid} /F`);
|
|
753
726
|
console.error(`[mesh] killed PID ${pid} on port ${port}`);
|
|
754
727
|
} catch {
|
|
755
728
|
}
|
|
756
729
|
}
|
|
757
730
|
} else {
|
|
758
|
-
|
|
731
|
+
await execAsync(`fuser -k ${port}/tcp`);
|
|
759
732
|
}
|
|
760
733
|
} catch {
|
|
761
734
|
}
|
|
@@ -971,49 +944,6 @@ function registerReplyTool(server, client) {
|
|
|
971
944
|
});
|
|
972
945
|
}
|
|
973
946
|
|
|
974
|
-
// src/presentation/mcp/tools/history.tool.ts
|
|
975
|
-
var HISTORY_DESCRIPTION = `Show all questions and answers exchanged this session \u2014 both sent and received.
|
|
976
|
-
|
|
977
|
-
WHEN TO USE:
|
|
978
|
-
- To review what was already discussed before asking a follow-up question
|
|
979
|
-
- To check if a previously sent question has been answered yet
|
|
980
|
-
- To find a questionId you need to reference
|
|
981
|
-
- To catch up on received questions you may have missed
|
|
982
|
-
|
|
983
|
-
OUTPUT FORMAT:
|
|
984
|
-
- \u2192 peer: question you sent, with their answer below
|
|
985
|
-
- \u2190 peer: question they sent you, with your reply below
|
|
986
|
-
- (no answer yet) means the question is still waiting for a response`;
|
|
987
|
-
function registerHistoryTool(server, client) {
|
|
988
|
-
server.tool("history", HISTORY_DESCRIPTION, {}, async () => {
|
|
989
|
-
const entries = client.getHistory();
|
|
990
|
-
if (entries.length === 0) {
|
|
991
|
-
return {
|
|
992
|
-
content: [{ type: "text", text: "No questions exchanged yet this session." }]
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
const unanswered = entries.filter((e) => !e.answer);
|
|
996
|
-
const lines = entries.map((e) => {
|
|
997
|
-
const time = new Date(e.askedAt).toLocaleTimeString();
|
|
998
|
-
if (e.direction === "sent") {
|
|
999
|
-
const answerLine = e.answer ? ` \u21B3 ${e.peer}: ${e.answer}` : ` \u21B3 (no answer yet)`;
|
|
1000
|
-
return `[${time}] \u2192 ${e.peer}: ${e.question}
|
|
1001
|
-
${answerLine}`;
|
|
1002
|
-
} else {
|
|
1003
|
-
const answerLine = e.answer ? ` \u21B3 you: ${e.answer}` : ` \u21B3 (not replied yet \u2014 use reply() if you haven't answered)`;
|
|
1004
|
-
return `[${time}] \u2190 ${e.peer}: ${e.question}
|
|
1005
|
-
${answerLine}`;
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1008
|
-
const summary = unanswered.length > 0 ? `
|
|
1009
|
-
|
|
1010
|
-
\u26A0 ${unanswered.length} question(s) still waiting for a response.` : "";
|
|
1011
|
-
return {
|
|
1012
|
-
content: [{ type: "text", text: lines.join("\n\n") + summary }]
|
|
1013
|
-
};
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
947
|
// src/presentation/mcp/tools/status.tool.ts
|
|
1018
948
|
var STATUS_DESCRIPTION = `Show your identity and network address on the collaboration network.
|
|
1019
949
|
|
|
@@ -1082,7 +1012,6 @@ function createMcpServer(options) {
|
|
|
1082
1012
|
registerStatusTool(server, client);
|
|
1083
1013
|
registerAskTool(server, client);
|
|
1084
1014
|
registerReplyTool(server, client);
|
|
1085
|
-
registerHistoryTool(server, client);
|
|
1086
1015
|
return server;
|
|
1087
1016
|
}
|
|
1088
1017
|
async function startMcpServer(options) {
|