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