@gonzih/cc-tg 0.9.14 → 0.9.15

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/bot.js CHANGED
@@ -952,8 +952,9 @@ export class CcTgBot {
952
952
  await this.bot.sendMessage(chatId, "Clearing npx cache and reloading MCP...");
953
953
  try {
954
954
  const home = process.env.HOME ?? "~";
955
- execSync(`rm -rf "${home}/.npm/_npx/"`, { encoding: "utf8", shell: "/bin/sh" });
956
- console.log("[mcp] cleared ~/.npm/_npx/");
955
+ const npmBase = process.env.npm_config_cache ? join(process.env.npm_config_cache, "..") : `${home}/.npm`;
956
+ execSync(`rm -rf "${npmBase}/_npx/"`, { encoding: "utf8", shell: "/bin/sh" });
957
+ console.log(`[mcp] cleared ${npmBase}/_npx/`);
957
958
  }
958
959
  catch (err) {
959
960
  await this.bot.sendMessage(chatId, `Warning: failed to clear npx cache: ${err.message}`);
@@ -995,10 +996,14 @@ export class CcTgBot {
995
996
  }
996
997
  async handleClearNpxCache(chatId) {
997
998
  const home = process.env.HOME ?? "/tmp";
999
+ // Use the isolated npm cache dir set in the plist (npm_config_cache), not hardcoded ~/.npm
1000
+ const npmBase = process.env.npm_config_cache
1001
+ ? join(process.env.npm_config_cache, "..")
1002
+ : `${home}/.npm`;
998
1003
  const cleared = [];
999
1004
  const failed = [];
1000
1005
  // Clear both npx execution cache and full npm package cache
1001
- for (const dir of [`${home}/.npm/_npx`, `${home}/.npm/cache`]) {
1006
+ for (const dir of [`${npmBase}/_npx`, `${npmBase}/cache`]) {
1002
1007
  try {
1003
1008
  execSync(`rm -rf "${dir}"`, { encoding: "utf8", shell: "/bin/sh" });
1004
1009
  cleared.push(dir.replace(home, "~"));
@@ -1022,8 +1027,12 @@ export class CcTgBot {
1022
1027
  await this.bot.sendMessage(chatId, "Clearing cache and restarting... brb.");
1023
1028
  await new Promise(resolve => setTimeout(resolve, 300));
1024
1029
  // Clear npm caches before restart so launchd brings up fresh version
1030
+ // Use isolated npm_config_cache path from plist, not hardcoded ~/.npm
1025
1031
  const home = process.env.HOME ?? "/tmp";
1026
- for (const dir of [`${home}/.npm/_npx`, `${home}/.npm/cache`]) {
1032
+ const npmBase = process.env.npm_config_cache
1033
+ ? join(process.env.npm_config_cache, "..")
1034
+ : `${home}/.npm`;
1035
+ for (const dir of [`${npmBase}/_npx`, `${npmBase}/cache`]) {
1027
1036
  try {
1028
1037
  execSync(`rm -rf "${dir}"`, { shell: "/bin/sh" });
1029
1038
  }
@@ -1073,18 +1082,24 @@ export class CcTgBot {
1073
1082
  callCcAgentTool(toolName, args = {}) {
1074
1083
  return new Promise((resolve) => {
1075
1084
  let settled = false;
1085
+ let procRef = null;
1076
1086
  const done = (val) => {
1077
1087
  if (!settled) {
1078
1088
  settled = true;
1089
+ try {
1090
+ procRef?.kill();
1091
+ }
1092
+ catch { }
1079
1093
  resolve(val);
1080
1094
  }
1081
1095
  };
1082
1096
  let proc;
1083
1097
  try {
1084
- proc = spawn("npx", ["-y", "@gonzih/cc-agent@latest"], {
1098
+ proc = spawn("npx", ["--prefer-online", "-y", "@gonzih/cc-agent@latest"], {
1085
1099
  env: { ...process.env },
1086
1100
  stdio: ["pipe", "pipe", "pipe"],
1087
1101
  });
1102
+ procRef = proc;
1088
1103
  }
1089
1104
  catch (err) {
1090
1105
  console.error("[mcp] failed to spawn cc-agent:", err.message);
package/dist/index.js CHANGED
@@ -18,12 +18,18 @@ import { createServer, createConnection } from "net";
18
18
  import { unlinkSync } from "fs";
19
19
  import { tmpdir } from "os";
20
20
  import { join } from "path";
21
+ import { createHash } from "crypto";
21
22
  import { CcTgBot } from "./bot.js";
22
- const LOCK_SOCKET = join(tmpdir(), "cc-tg.sock");
23
- function acquireLock() {
23
+ // Derive socket path from token hash so multiple instances (feral/law/simorgh)
24
+ // never collide, and the path is stable across restarts on the same machine.
25
+ function lockSocketPath(token) {
26
+ const hash = createHash("sha256").update(token).digest("hex").slice(0, 12);
27
+ return join(tmpdir(), `cc-tg-${hash}.sock`);
28
+ }
29
+ function acquireLock(socketPath) {
24
30
  return new Promise((resolve) => {
25
31
  const server = createServer();
26
- server.listen(LOCK_SOCKET, () => {
32
+ server.listen(socketPath, () => {
27
33
  // Bound successfully — we own the lock. Socket auto-released on any exit incl. SIGKILL.
28
34
  resolve(true);
29
35
  });
@@ -33,7 +39,7 @@ function acquireLock() {
33
39
  return;
34
40
  }
35
41
  // Socket path exists — probe if anything is actually listening
36
- const probe = createConnection(LOCK_SOCKET);
42
+ const probe = createConnection(socketPath);
37
43
  probe.on("connect", () => {
38
44
  probe.destroy();
39
45
  console.error("[cc-tg] Another instance is already running. Exiting.");
@@ -42,20 +48,16 @@ function acquireLock() {
42
48
  probe.on("error", () => {
43
49
  // Nothing listening — stale socket, remove and retry
44
50
  try {
45
- unlinkSync(LOCK_SOCKET);
51
+ unlinkSync(socketPath);
46
52
  }
47
53
  catch { }
48
54
  const retry = createServer();
49
- retry.listen(LOCK_SOCKET, () => resolve(true));
55
+ retry.listen(socketPath, () => resolve(true));
50
56
  retry.on("error", () => resolve(true)); // give up on lock, just start
51
57
  });
52
58
  });
53
59
  });
54
60
  }
55
- const lockAcquired = await acquireLock();
56
- if (!lockAcquired) {
57
- process.exit(1);
58
- }
59
61
  function required(name) {
60
62
  const val = process.env[name];
61
63
  if (!val) {
@@ -76,6 +78,13 @@ Or add to your shell profile / .env file.
76
78
  return val;
77
79
  }
78
80
  const telegramToken = required("TELEGRAM_BOT_TOKEN");
81
+ // Acquire lock before doing anything else. Socket derived from token hash so
82
+ // multiple instances (different bots / users) never share the same socket.
83
+ const LOCK_SOCKET = lockSocketPath(telegramToken);
84
+ const lockAcquired = await acquireLock(LOCK_SOCKET);
85
+ if (!lockAcquired) {
86
+ process.exit(1);
87
+ }
79
88
  // Accept CLAUDE_CODE_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, or ANTHROPIC_API_KEY
80
89
  const claudeToken = process.env.CLAUDE_CODE_TOKEN ??
81
90
  process.env.CLAUDE_CODE_OAUTH_TOKEN ??
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-tg",
3
- "version": "0.9.14",
3
+ "version": "0.9.15",
4
4
  "description": "Claude Code Telegram bot — chat with Claude Code via Telegram",
5
5
  "type": "module",
6
6
  "bin": {