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