@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/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 { execSync, execFile, spawn } from 'child_process';
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.set(senderName, answerMsg);
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()].filter((q) => !q.answered).map((q) => ({
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, pending);
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 out = execSync(
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 out.split("\n")) {
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
- execSync(`taskkill /PID ${pid} /F`, { stdio: "ignore" });
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
- execSync(`fuser -k ${port}/tcp`, { stdio: "ignore" });
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) {