@dolusoft/claude-collab 1.11.2 → 1.11.4
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 +32 -92
- package/dist/cli.js.map +1 -1
- package/dist/mcp-main.js +32 -92
- 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';
|
|
@@ -57,7 +58,7 @@ public class ConInject {
|
|
|
57
58
|
string lpFileName, uint dwDesiredAccess, uint dwShareMode,
|
|
58
59
|
IntPtr lpSecurityAttributes, uint dwCreationDisposition,
|
|
59
60
|
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
|
|
60
|
-
[DllImport("kernel32.dll")] public static extern bool WriteConsoleInput(
|
|
61
|
+
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern bool WriteConsoleInput(
|
|
61
62
|
IntPtr hIn, INPUT_RECORD[] buf, uint len, out uint written);
|
|
62
63
|
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr h);
|
|
63
64
|
|
|
@@ -263,12 +264,14 @@ 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;
|
|
269
271
|
myName = "";
|
|
270
272
|
running = false;
|
|
271
273
|
firewallOpened = false;
|
|
274
|
+
cachedLocalIps = [];
|
|
272
275
|
// One connection per peer (inbound or outbound — whichever was established first)
|
|
273
276
|
peerConnections = /* @__PURE__ */ new Map();
|
|
274
277
|
// Reverse map: ws → peer name (only for registered connections)
|
|
@@ -279,12 +282,11 @@ var MeshNode = class {
|
|
|
279
282
|
connectingPeers = /* @__PURE__ */ new Set();
|
|
280
283
|
incomingQuestions = /* @__PURE__ */ new Map();
|
|
281
284
|
receivedAnswers = /* @__PURE__ */ new Map();
|
|
282
|
-
sentQuestions = /* @__PURE__ */ new Map();
|
|
283
285
|
questionToSender = /* @__PURE__ */ new Map();
|
|
284
286
|
pendingHandlers = /* @__PURE__ */ new Set();
|
|
285
287
|
// Push-based answer resolution: questionId → resolve callback
|
|
286
288
|
answerWaiters = /* @__PURE__ */ new Map();
|
|
287
|
-
// Answers queued for offline peers: peerName → AnswerMsg (delivered on reconnect)
|
|
289
|
+
// Answers queued for offline peers: peerName → AnswerMsg[] (delivered on reconnect)
|
|
288
290
|
pendingOutboundAnswers = /* @__PURE__ */ new Map();
|
|
289
291
|
// ---------------------------------------------------------------------------
|
|
290
292
|
// ICollabClient implementation
|
|
@@ -300,6 +302,7 @@ var MeshNode = class {
|
|
|
300
302
|
}
|
|
301
303
|
async join(name, displayName) {
|
|
302
304
|
this.myName = name;
|
|
305
|
+
this.cachedLocalIps = this.scanLocalIps();
|
|
303
306
|
await this.startServer();
|
|
304
307
|
return {
|
|
305
308
|
memberId: v4(),
|
|
@@ -345,7 +348,6 @@ var MeshNode = class {
|
|
|
345
348
|
});
|
|
346
349
|
ws.on("close", () => {
|
|
347
350
|
clearTimeout(timeout);
|
|
348
|
-
this.connectingPeers.delete(ip);
|
|
349
351
|
const name = this.wsToName.get(ws);
|
|
350
352
|
if (name) {
|
|
351
353
|
this.wsToName.delete(ws);
|
|
@@ -357,7 +359,6 @@ var MeshNode = class {
|
|
|
357
359
|
});
|
|
358
360
|
ws.on("error", (err) => {
|
|
359
361
|
clearTimeout(timeout);
|
|
360
|
-
this.connectingPeers.delete(ip);
|
|
361
362
|
reject(new Error(`Failed to connect to ${ip}:${FIXED_PORT} \u2014 ${err.message}`));
|
|
362
363
|
});
|
|
363
364
|
});
|
|
@@ -368,7 +369,6 @@ var MeshNode = class {
|
|
|
368
369
|
throw new Error(`Peer "${toPeer}" is not connected. Use status() to see who's online.`);
|
|
369
370
|
}
|
|
370
371
|
const questionId = v4();
|
|
371
|
-
this.sentQuestions.set(questionId, { toPeer, content, askedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
372
372
|
const ackPromise = this.waitForResponse(
|
|
373
373
|
(m) => m.type === "ASK_ACK" && m.questionId === questionId,
|
|
374
374
|
5e3
|
|
@@ -379,7 +379,10 @@ var MeshNode = class {
|
|
|
379
379
|
}
|
|
380
380
|
waitForAnswer(questionId, timeoutMs) {
|
|
381
381
|
const cached = this.receivedAnswers.get(questionId);
|
|
382
|
-
if (cached)
|
|
382
|
+
if (cached) {
|
|
383
|
+
this.receivedAnswers.delete(questionId);
|
|
384
|
+
return Promise.resolve(this.formatAnswer(questionId, cached));
|
|
385
|
+
}
|
|
383
386
|
return new Promise((resolve) => {
|
|
384
387
|
const timeout = setTimeout(() => {
|
|
385
388
|
this.answerWaiters.delete(questionId);
|
|
@@ -393,7 +396,9 @@ var MeshNode = class {
|
|
|
393
396
|
}
|
|
394
397
|
async checkAnswer(questionId) {
|
|
395
398
|
const cached = this.receivedAnswers.get(questionId);
|
|
396
|
-
|
|
399
|
+
if (!cached) return null;
|
|
400
|
+
this.receivedAnswers.delete(questionId);
|
|
401
|
+
return this.formatAnswer(questionId, cached);
|
|
397
402
|
}
|
|
398
403
|
formatAnswer(questionId, cached) {
|
|
399
404
|
return {
|
|
@@ -411,6 +416,8 @@ var MeshNode = class {
|
|
|
411
416
|
question.answerContent = content;
|
|
412
417
|
question.answerFormat = format;
|
|
413
418
|
const senderName = this.questionToSender.get(questionId);
|
|
419
|
+
this.incomingQuestions.delete(questionId);
|
|
420
|
+
this.questionToSender.delete(questionId);
|
|
414
421
|
if (senderName) {
|
|
415
422
|
const answerMsg = {
|
|
416
423
|
type: "ANSWER",
|
|
@@ -424,7 +431,9 @@ var MeshNode = class {
|
|
|
424
431
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
425
432
|
this.sendToWs(ws, answerMsg);
|
|
426
433
|
} else {
|
|
427
|
-
this.pendingOutboundAnswers.
|
|
434
|
+
const queue = this.pendingOutboundAnswers.get(senderName) ?? [];
|
|
435
|
+
queue.push(answerMsg);
|
|
436
|
+
this.pendingOutboundAnswers.set(senderName, queue);
|
|
428
437
|
console.error(`[mesh] "${senderName}" is offline, answer queued for delivery on reconnect`);
|
|
429
438
|
}
|
|
430
439
|
}
|
|
@@ -432,7 +441,7 @@ var MeshNode = class {
|
|
|
432
441
|
}
|
|
433
442
|
async getInbox() {
|
|
434
443
|
const now = Date.now();
|
|
435
|
-
const questions = [...this.incomingQuestions.values()].
|
|
444
|
+
const questions = [...this.incomingQuestions.values()].map((q) => ({
|
|
436
445
|
questionId: q.questionId,
|
|
437
446
|
from: { displayName: `${q.fromName} Claude`, teamName: q.fromName },
|
|
438
447
|
content: q.content,
|
|
@@ -452,6 +461,9 @@ var MeshNode = class {
|
|
|
452
461
|
};
|
|
453
462
|
}
|
|
454
463
|
getLocalIps() {
|
|
464
|
+
return this.cachedLocalIps;
|
|
465
|
+
}
|
|
466
|
+
scanLocalIps() {
|
|
455
467
|
const result = [];
|
|
456
468
|
const interfaces = os.networkInterfaces();
|
|
457
469
|
for (const iface of Object.values(interfaces)) {
|
|
@@ -462,31 +474,6 @@ var MeshNode = class {
|
|
|
462
474
|
}
|
|
463
475
|
return result;
|
|
464
476
|
}
|
|
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
477
|
async disconnect() {
|
|
491
478
|
for (const ws of this.peerConnections.values()) ws.close();
|
|
492
479
|
this.peerConnections.clear();
|
|
@@ -617,7 +604,6 @@ var MeshNode = class {
|
|
|
617
604
|
// ---------------------------------------------------------------------------
|
|
618
605
|
/** Handles messages on inbound connections (server side — we know the remote IP). */
|
|
619
606
|
handleInboundMessage(ws, remoteIp, msg) {
|
|
620
|
-
for (const handler of this.pendingHandlers) handler(msg);
|
|
621
607
|
if (msg.type === "HELLO") {
|
|
622
608
|
if (this.peerConnections.has(msg.name)) {
|
|
623
609
|
ws.terminate();
|
|
@@ -672,11 +658,12 @@ var MeshNode = class {
|
|
|
672
658
|
answeredAt: msg.answeredAt,
|
|
673
659
|
fromName: msg.from
|
|
674
660
|
};
|
|
675
|
-
this.receivedAnswers.set(msg.questionId, record);
|
|
676
661
|
const waiter = this.answerWaiters.get(msg.questionId);
|
|
677
662
|
if (waiter) {
|
|
678
663
|
this.answerWaiters.delete(msg.questionId);
|
|
679
664
|
waiter(this.formatAnswer(msg.questionId, record));
|
|
665
|
+
} else {
|
|
666
|
+
this.receivedAnswers.set(msg.questionId, record);
|
|
680
667
|
}
|
|
681
668
|
}
|
|
682
669
|
break;
|
|
@@ -705,10 +692,10 @@ var MeshNode = class {
|
|
|
705
692
|
}
|
|
706
693
|
deliverPendingAnswer(peerName, ws) {
|
|
707
694
|
const pending = this.pendingOutboundAnswers.get(peerName);
|
|
708
|
-
if (pending) {
|
|
695
|
+
if (pending && pending.length > 0) {
|
|
709
696
|
this.pendingOutboundAnswers.delete(peerName);
|
|
710
|
-
this.sendToWs(ws,
|
|
711
|
-
console.error(`[mesh] delivered queued answer to "${peerName}" after reconnect`);
|
|
697
|
+
for (const msg of pending) this.sendToWs(ws, msg);
|
|
698
|
+
console.error(`[mesh] delivered ${pending.length} queued answer(s) to "${peerName}" after reconnect`);
|
|
712
699
|
}
|
|
713
700
|
}
|
|
714
701
|
sendToWs(ws, msg) {
|
|
@@ -736,12 +723,9 @@ var MeshNode = class {
|
|
|
736
723
|
async function killProcessOnPort(port) {
|
|
737
724
|
try {
|
|
738
725
|
if (process.platform === "win32") {
|
|
739
|
-
const
|
|
740
|
-
`netstat -ano | findstr ":${port} "`,
|
|
741
|
-
{ encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
|
|
742
|
-
);
|
|
726
|
+
const { stdout } = await execAsync(`netstat -ano | findstr ":${port} "`);
|
|
743
727
|
const pids = /* @__PURE__ */ new Set();
|
|
744
|
-
for (const line of
|
|
728
|
+
for (const line of stdout.split("\n")) {
|
|
745
729
|
const parts = line.trim().split(/\s+/);
|
|
746
730
|
if (parts.length >= 5 && parts[3] === "LISTENING" && parts[4]) {
|
|
747
731
|
pids.add(parts[4]);
|
|
@@ -749,13 +733,13 @@ async function killProcessOnPort(port) {
|
|
|
749
733
|
}
|
|
750
734
|
for (const pid of pids) {
|
|
751
735
|
try {
|
|
752
|
-
|
|
736
|
+
await execAsync(`taskkill /PID ${pid} /F`);
|
|
753
737
|
console.error(`[mesh] killed PID ${pid} on port ${port}`);
|
|
754
738
|
} catch {
|
|
755
739
|
}
|
|
756
740
|
}
|
|
757
741
|
} else {
|
|
758
|
-
|
|
742
|
+
await execAsync(`fuser -k ${port}/tcp`);
|
|
759
743
|
}
|
|
760
744
|
} catch {
|
|
761
745
|
}
|
|
@@ -971,49 +955,6 @@ function registerReplyTool(server, client) {
|
|
|
971
955
|
});
|
|
972
956
|
}
|
|
973
957
|
|
|
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
958
|
// src/presentation/mcp/tools/status.tool.ts
|
|
1018
959
|
var STATUS_DESCRIPTION = `Show your identity and network address on the collaboration network.
|
|
1019
960
|
|
|
@@ -1082,7 +1023,6 @@ function createMcpServer(options) {
|
|
|
1082
1023
|
registerStatusTool(server, client);
|
|
1083
1024
|
registerAskTool(server, client);
|
|
1084
1025
|
registerReplyTool(server, client);
|
|
1085
|
-
registerHistoryTool(server, client);
|
|
1086
1026
|
return server;
|
|
1087
1027
|
}
|
|
1088
1028
|
async function startMcpServer(options) {
|