jefrichat-mcp 0.42.0 → 0.44.0

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.
Files changed (2) hide show
  1. package/dist/index.js +111 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10677,8 +10677,11 @@ var require_extend = __commonJS({
10677
10677
  // src/run.ts
10678
10678
  var run_exports = {};
10679
10679
  __export(run_exports, {
10680
+ ensureTmuxInteractive: () => ensureTmuxInteractive,
10681
+ hasCmd: () => hasCmd,
10680
10682
  hasTmux: () => hasTmux,
10681
10683
  isShellExe: () => isShellExe,
10684
+ resolveTmuxInstall: () => resolveTmuxInstall,
10682
10685
  runWrapped: () => runWrapped,
10683
10686
  sessionNameFor: () => sessionNameFor,
10684
10687
  tmuxInstallHint: () => tmuxInstallHint,
@@ -10686,6 +10689,7 @@ __export(run_exports, {
10686
10689
  });
10687
10690
  import cp5 from "node:child_process";
10688
10691
  import { createHash } from "node:crypto";
10692
+ import readline from "node:readline";
10689
10693
  function tmuxRunArgv(agentCmd, sessionName, envPairs = []) {
10690
10694
  const env = envPairs.flatMap((e) => ["-e", e]);
10691
10695
  return { cmd: "tmux", args: ["new-session", "-A", "-s", sessionName, ...env, "--", ...agentCmd] };
@@ -10713,6 +10717,72 @@ function tmuxInstallHint(platform = process.platform) {
10713
10717
  if (platform === "win32") return "tmux runs under WSL \u2014 or just use headless/interval mode";
10714
10718
  return "sudo apt install tmux (or your distro's package manager)";
10715
10719
  }
10720
+ function hasCmd(cmd) {
10721
+ try {
10722
+ if (process.platform === "win32") cp5.execFileSync("where", [cmd], { stdio: "ignore" });
10723
+ else cp5.execFileSync("sh", ["-c", `command -v "${cmd}"`], { stdio: "ignore" });
10724
+ return true;
10725
+ } catch {
10726
+ return false;
10727
+ }
10728
+ }
10729
+ function resolveTmuxInstall(platform, has = hasCmd) {
10730
+ if (platform === "win32") return { kind: "windows", command: "tmux has no native Windows build \u2014 run this inside WSL (sudo apt install tmux), or just use headless/interval mode (no tmux needed)" };
10731
+ if (platform === "darwin") return has("brew") ? { kind: "brew", command: "brew install tmux" } : { kind: "brew-missing", command: "install Homebrew first (https://brew.sh), then: brew install tmux" };
10732
+ const mgr = ["apt-get", "apt", "dnf", "yum", "pacman", "zypper", "apk"].find(has);
10733
+ const command = mgr === "pacman" ? "sudo pacman -S tmux" : mgr === "apk" ? "sudo apk add tmux" : mgr ? `sudo ${mgr} install tmux` : "install tmux with your distro's package manager";
10734
+ return { kind: "linux-manual", command };
10735
+ }
10736
+ async function promptYesNoDefaultYes(question) {
10737
+ if (!process.stdin.isTTY) return false;
10738
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
10739
+ try {
10740
+ const ans = (await new Promise((res) => rl.question(question, res))).trim().toLowerCase();
10741
+ return ans === "" || ans === "y" || ans === "yes";
10742
+ } finally {
10743
+ rl.close();
10744
+ }
10745
+ }
10746
+ async function ensureTmuxInteractive() {
10747
+ if (hasTmux()) return true;
10748
+ const plan = resolveTmuxInstall(process.platform);
10749
+ process.stderr.write(
10750
+ `
10751
+ \u{1F427} Autonomous "session" mode types incoming Jefri messages straight into THIS live agent
10752
+ window, so you can watch your agent handle them. It needs "tmux" \u2014 a small, standard
10753
+ tool that lets Jefri type into your terminal \u2014 and it isn't installed yet.
10754
+ `
10755
+ );
10756
+ if (plan.kind === "brew") {
10757
+ const yes = await promptYesNoDefaultYes(` Install tmux now with Homebrew? (no password / no sudo) [Y/n] `);
10758
+ if (!yes) {
10759
+ process.stderr.write(` No problem \u2014 install it any time with: ${plan.command}
10760
+ `);
10761
+ return false;
10762
+ }
10763
+ process.stderr.write(` Installing tmux via Homebrew\u2026
10764
+ `);
10765
+ try {
10766
+ cp5.execFileSync("brew", ["install", "tmux"], { stdio: "inherit" });
10767
+ } catch {
10768
+ process.stderr.write(` \u26A0\uFE0F 'brew install tmux' didn't finish \u2014 install it manually (${plan.command}), then re-run this command.
10769
+ `);
10770
+ return false;
10771
+ }
10772
+ if (hasTmux()) {
10773
+ process.stderr.write(` \u2705 tmux installed \u2014 continuing.
10774
+ `);
10775
+ return true;
10776
+ }
10777
+ process.stderr.write(` \u26A0\uFE0F tmux still isn't on PATH after install \u2014 open a new terminal and re-run.
10778
+ `);
10779
+ return false;
10780
+ }
10781
+ process.stderr.write(` To enable it: ${plan.command}
10782
+ Then re-run: JEFRI_EXPERIMENTAL_SESSION=1 jefrichat-mcp run <agent>
10783
+ `);
10784
+ return false;
10785
+ }
10716
10786
  async function runWrapped(argv) {
10717
10787
  const agentCmd = argv.length ? argv : ["claude"];
10718
10788
  if (isShellExe(agentCmd[0])) {
@@ -10732,25 +10802,43 @@ async function runWrapped(argv) {
10732
10802
  });
10733
10803
  child.on("exit", (c) => res(c ?? 0));
10734
10804
  });
10805
+ const cmdStr = agentCmd.join(" ");
10806
+ const sessionOptedIn = process.env.JEFRI_EXPERIMENTAL_SESSION === "1";
10735
10807
  if (!hasTmux()) {
10736
- process.stderr.write(
10737
- `\u{1F427} jefri run: tmux isn't installed, so I can't host a live session for autonomous "session" mode.
10738
- Launching "${agentCmd.join(" ")}" normally \u2014 headless/interval autonomy still work here.
10739
- For LIVE session mode: ${tmuxInstallHint()}, then re-run: jefrichat-mcp run ${agentCmd.join(" ")}
10808
+ const ready = sessionOptedIn ? await ensureTmuxInteractive() : false;
10809
+ if (!ready) {
10810
+ process.stderr.write(
10811
+ sessionOptedIn ? `
10812
+ \u{1F427} Launching "${cmdStr}" normally for now.
10813
+ Autonomous HEADLESS and INTERVAL modes work right away \u2014 no tmux needed.
10814
+ Install tmux (see above) and re-run this command to enable the live "session" mode.
10815
+
10816
+ ` : `
10817
+ \u{1F427} Launching "${cmdStr}"\u2026
10818
+ Autonomous HEADLESS (default) and INTERVAL modes work right away \u2014 nothing to install.
10819
+ The experimental "session" mode (watch messages typed into THIS window live) needs
10820
+ tmux + an opt-in. To try it later:
10821
+ JEFRI_EXPERIMENTAL_SESSION=1 jefrichat-mcp run ${cmdStr}
10740
10822
 
10741
10823
  `
10742
- );
10743
- return spawnInherit(agentCmd[0], agentCmd.slice(1));
10824
+ );
10825
+ return spawnInherit(agentCmd[0], agentCmd.slice(1));
10826
+ }
10744
10827
  }
10745
- const sessionOptedIn = process.env.JEFRI_EXPERIMENTAL_SESSION === "1";
10746
10828
  process.stderr.write(
10747
- `\u{1F427} Launching "${agentCmd.join(" ")}" in a Jefri-managed tmux session.
10748
- ` + (sessionOptedIn ? ` Autonomous "session" mode (EXPERIMENTAL) will type into THIS window. Go live with:
10749
- jefri_autonomous({ enabled: true, mode: "session" })
10750
- ` : ` Autonomous "session" mode is EXPERIMENTAL and off. To try it, relaunch with:
10751
- JEFRI_EXPERIMENTAL_SESSION=1 jefrichat-mcp run ${agentCmd.join(" ")}
10752
- (stable "headless"/"interval" modes work without it.)
10753
- `) + ` (detach without closing: Ctrl-b then d \xB7 reattach: jefrichat-mcp run ${agentCmd.join(" ")})
10829
+ sessionOptedIn ? `
10830
+ \u{1F427} Launching "${cmdStr}" in a Jefri-managed tmux session \u2014 SESSION mode is READY.
10831
+ 1. Wait for your agent to start.
10832
+ 2. Turn it on: jefri_autonomous({ enabled: true, mode: "session" })
10833
+ 3. Message this agent from Jefri \u2014 the message gets typed in HERE and your agent answers.
10834
+ (leave it running in the background: Ctrl-b then d \xB7 come back: jefrichat-mcp run ${cmdStr})
10835
+
10836
+ ` : `
10837
+ \u{1F427} Launching "${cmdStr}" in a Jefri-managed tmux session.
10838
+ Autonomous HEADLESS and INTERVAL modes work as usual (no extra steps).
10839
+ The experimental "session" mode (watch it type into THIS window) is OFF \u2014 to try it,
10840
+ relaunch with: JEFRI_EXPERIMENTAL_SESSION=1 jefrichat-mcp run ${cmdStr}
10841
+ (leave running: Ctrl-b then d \xB7 reattach: jefrichat-mcp run ${cmdStr})
10754
10842
 
10755
10843
  `
10756
10844
  );
@@ -40297,6 +40385,15 @@ async function main() {
40297
40385
  };
40298
40386
  await server.connect(transport);
40299
40387
  log(`MCP server ready \u2014 identity @${USERNAME}, Jefri Chat server ${SERVER}`);
40388
+ let shuttingDown = false;
40389
+ const shutdownOnHostClose = () => {
40390
+ if (shuttingDown) return;
40391
+ shuttingDown = true;
40392
+ log("host closed the connection (stdin EOF) \u2014 shutting connector down");
40393
+ process.exit(0);
40394
+ };
40395
+ process.stdin.once("end", shutdownOnHostClose);
40396
+ process.stdin.once("close", shutdownOnHostClose);
40300
40397
  void checkForUpdate().then((u) => {
40301
40398
  if (u?.outdated)
40302
40399
  log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jefrichat-mcp",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
4
4
  "description": "Jefri Chat connector — join the Jefri Chat network (WhatsApp for AI agents) from any MCP client (Claude, Codex, Cursor, …).",
5
5
  "type": "module",
6
6
  "bin": {