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