@gonzih/cc-tg 0.2.18 ā 0.2.20
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.d.ts +2 -0
- package/dist/bot.js +131 -4
- package/package.json +1 -1
package/dist/bot.d.ts
CHANGED
|
@@ -38,10 +38,12 @@ export declare class CcTgBot {
|
|
|
38
38
|
/** Kill cc-agent PIDs with SIGTERM. Returns the list of killed PIDs. */
|
|
39
39
|
private killCcAgent;
|
|
40
40
|
private handleReloadMcp;
|
|
41
|
+
private handleMcpStatus;
|
|
41
42
|
private handleMcpVersion;
|
|
42
43
|
private handleClearNpxCache;
|
|
43
44
|
private handleRestart;
|
|
44
45
|
private handleGetFile;
|
|
46
|
+
private callCcAgentTool;
|
|
45
47
|
private killSession;
|
|
46
48
|
stop(): void;
|
|
47
49
|
}
|
package/dist/bot.js
CHANGED
|
@@ -6,7 +6,7 @@ import TelegramBot from "node-telegram-bot-api";
|
|
|
6
6
|
import { existsSync, createWriteStream, mkdirSync, statSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
7
7
|
import { resolve, basename, join } from "path";
|
|
8
8
|
import os from "os";
|
|
9
|
-
import { execSync } from "child_process";
|
|
9
|
+
import { execSync, spawn } from "child_process";
|
|
10
10
|
import https from "https";
|
|
11
11
|
import http from "http";
|
|
12
12
|
import { ClaudeProcess, extractText } from "./claude.js";
|
|
@@ -20,6 +20,7 @@ const BOT_COMMANDS = [
|
|
|
20
20
|
{ command: "help", description: "Show all available commands" },
|
|
21
21
|
{ command: "cron", description: "Manage cron jobs ā add/list/edit/remove/clear" },
|
|
22
22
|
{ command: "reload_mcp", description: "Restart the cc-agent MCP server process" },
|
|
23
|
+
{ command: "mcp_status", description: "Check MCP server connection status" },
|
|
23
24
|
{ command: "mcp_version", description: "Show cc-agent npm version and npx cache info" },
|
|
24
25
|
{ command: "clear_npx_cache", description: "Clear npx cache and restart MCP to pick up latest version" },
|
|
25
26
|
{ command: "restart", description: "Restart the bot process in-place" },
|
|
@@ -65,6 +66,28 @@ function formatCronCostFooter(usage) {
|
|
|
65
66
|
const cost = computeCostUsd(usage);
|
|
66
67
|
return `\nš° Cron cost: $${cost.toFixed(4)} (${formatTokens(usage.inputTokens)} in / ${formatTokens(usage.outputTokens)} out tokens)`;
|
|
67
68
|
}
|
|
69
|
+
function formatAgentCostSummary(text) {
|
|
70
|
+
try {
|
|
71
|
+
const data = JSON.parse(text);
|
|
72
|
+
const totalCost = (data.total_cost_usd ?? data.total_cost ?? 0);
|
|
73
|
+
const totalJobs = (data.total_jobs ?? data.job_count ?? 0);
|
|
74
|
+
const byRepo = (data.by_repo ?? []);
|
|
75
|
+
const lines = [
|
|
76
|
+
"š¤ Agent jobs (all time)",
|
|
77
|
+
`Total: $${totalCost.toFixed(2)} across ${totalJobs} jobs`,
|
|
78
|
+
];
|
|
79
|
+
for (const entry of byRepo) {
|
|
80
|
+
const repo = (entry.repo ?? entry.repository ?? "unknown");
|
|
81
|
+
const cost = (entry.cost_usd ?? entry.cost ?? 0);
|
|
82
|
+
const jobs = (entry.job_count ?? entry.jobs ?? 0);
|
|
83
|
+
lines.push(` ${repo}: $${cost.toFixed(2)} (${jobs} jobs)`);
|
|
84
|
+
}
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return `š¤ Agent jobs (all time)\n${text}`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
68
91
|
class CostStore {
|
|
69
92
|
costs = new Map();
|
|
70
93
|
storePath;
|
|
@@ -214,6 +237,11 @@ export class CcTgBot {
|
|
|
214
237
|
await this.handleReloadMcp(chatId);
|
|
215
238
|
return;
|
|
216
239
|
}
|
|
240
|
+
// /mcp_status ā run `claude mcp list` and show connection status
|
|
241
|
+
if (text === "/mcp_status") {
|
|
242
|
+
await this.handleMcpStatus(chatId);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
217
245
|
// /mcp_version ā show published npm version and cached npx entries
|
|
218
246
|
if (text === "/mcp_version") {
|
|
219
247
|
await this.handleMcpVersion(chatId);
|
|
@@ -237,7 +265,17 @@ export class CcTgBot {
|
|
|
237
265
|
// /cost ā show session token usage and cost
|
|
238
266
|
if (text === "/cost") {
|
|
239
267
|
const cost = this.costStore.get(chatId);
|
|
240
|
-
|
|
268
|
+
let reply = formatCostReport(cost);
|
|
269
|
+
try {
|
|
270
|
+
const rawSummary = await this.callCcAgentTool("cost_summary");
|
|
271
|
+
if (rawSummary) {
|
|
272
|
+
reply += "\n\n" + formatAgentCostSummary(rawSummary);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
console.error("[cost] cc-agent cost_summary failed:", err.message);
|
|
277
|
+
}
|
|
278
|
+
await this.bot.sendMessage(chatId, reply);
|
|
241
279
|
return;
|
|
242
280
|
}
|
|
243
281
|
const session = this.getOrCreateSession(chatId);
|
|
@@ -809,12 +847,30 @@ export class CcTgBot {
|
|
|
809
847
|
return pids;
|
|
810
848
|
}
|
|
811
849
|
async handleReloadMcp(chatId) {
|
|
850
|
+
await this.bot.sendMessage(chatId, "Clearing npx cache and reloading MCP...");
|
|
851
|
+
try {
|
|
852
|
+
const home = process.env.HOME ?? "~";
|
|
853
|
+
execSync(`rm -rf "${home}/.npm/_npx/"`, { encoding: "utf8", shell: "/bin/sh" });
|
|
854
|
+
console.log("[mcp] cleared ~/.npm/_npx/");
|
|
855
|
+
}
|
|
856
|
+
catch (err) {
|
|
857
|
+
await this.bot.sendMessage(chatId, `Warning: failed to clear npx cache: ${err.message}`);
|
|
858
|
+
}
|
|
812
859
|
const pids = this.killCcAgent();
|
|
813
860
|
if (pids.length === 0) {
|
|
814
|
-
await this.bot.sendMessage(chatId, "No cc-agent process found
|
|
861
|
+
await this.bot.sendMessage(chatId, "NPX cache cleared. No cc-agent process found ā MCP will start fresh on the next agent call.");
|
|
815
862
|
return;
|
|
816
863
|
}
|
|
817
|
-
await this.bot.sendMessage(chatId, `Sent SIGTERM to cc-agent (pid${pids.length > 1 ? "s" : ""}: ${pids.join(", ")}).\nMCP restarted. New process will load on next agent call.`);
|
|
864
|
+
await this.bot.sendMessage(chatId, `NPX cache cleared. Sent SIGTERM to cc-agent (pid${pids.length > 1 ? "s" : ""}: ${pids.join(", ")}).\nMCP restarted. New process will load on next agent call.`);
|
|
865
|
+
}
|
|
866
|
+
async handleMcpStatus(chatId) {
|
|
867
|
+
try {
|
|
868
|
+
const output = execSync("claude mcp list", { encoding: "utf8", shell: "/bin/sh" }).trim();
|
|
869
|
+
await this.bot.sendMessage(chatId, `MCP server status:\n\n${output || "(no output)"}`);
|
|
870
|
+
}
|
|
871
|
+
catch (err) {
|
|
872
|
+
await this.bot.sendMessage(chatId, `Failed to run claude mcp list: ${err.message}`);
|
|
873
|
+
}
|
|
818
874
|
}
|
|
819
875
|
async handleMcpVersion(chatId) {
|
|
820
876
|
let npmVersion = "unknown";
|
|
@@ -890,6 +946,77 @@ export class CcTgBot {
|
|
|
890
946
|
}
|
|
891
947
|
await this.bot.sendDocument(chatId, filePath);
|
|
892
948
|
}
|
|
949
|
+
callCcAgentTool(toolName, args = {}) {
|
|
950
|
+
return new Promise((resolve) => {
|
|
951
|
+
let settled = false;
|
|
952
|
+
const done = (val) => {
|
|
953
|
+
if (!settled) {
|
|
954
|
+
settled = true;
|
|
955
|
+
resolve(val);
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
let proc;
|
|
959
|
+
try {
|
|
960
|
+
proc = spawn("npx", ["-y", "@gonzih/cc-agent@latest"], {
|
|
961
|
+
env: { ...process.env },
|
|
962
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
catch (err) {
|
|
966
|
+
console.error("[mcp] failed to spawn cc-agent:", err.message);
|
|
967
|
+
done(null);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
const timeout = setTimeout(() => {
|
|
971
|
+
console.warn("[mcp] cc-agent tool call timed out");
|
|
972
|
+
proc.kill();
|
|
973
|
+
done(null);
|
|
974
|
+
}, 30_000);
|
|
975
|
+
let buffer = "";
|
|
976
|
+
const sendMsg = (msg) => { proc.stdin.write(JSON.stringify(msg) + "\n"); };
|
|
977
|
+
sendMsg({
|
|
978
|
+
jsonrpc: "2.0", id: 1, method: "initialize",
|
|
979
|
+
params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "cc-tg", version: "1.0.0" } },
|
|
980
|
+
});
|
|
981
|
+
proc.stdout.on("data", (chunk) => {
|
|
982
|
+
buffer += chunk.toString();
|
|
983
|
+
const lines = buffer.split("\n");
|
|
984
|
+
buffer = lines.pop() ?? "";
|
|
985
|
+
for (const line of lines) {
|
|
986
|
+
if (!line.trim())
|
|
987
|
+
continue;
|
|
988
|
+
try {
|
|
989
|
+
const msg = JSON.parse(line);
|
|
990
|
+
if (msg.id === 1 && "result" in msg) {
|
|
991
|
+
sendMsg({ jsonrpc: "2.0", method: "notifications/initialized" });
|
|
992
|
+
sendMsg({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: toolName, arguments: args } });
|
|
993
|
+
}
|
|
994
|
+
else if (msg.id === 2) {
|
|
995
|
+
clearTimeout(timeout);
|
|
996
|
+
if (msg.error) {
|
|
997
|
+
console.error("[mcp] cost_summary error:", JSON.stringify(msg.error));
|
|
998
|
+
proc.kill();
|
|
999
|
+
done(null);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
const result = msg.result;
|
|
1003
|
+
const content = result?.content;
|
|
1004
|
+
const text = (content ?? []).filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
1005
|
+
proc.kill();
|
|
1006
|
+
done(text || null);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
catch { /* ignore non-JSON lines */ }
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
proc.on("error", (err) => {
|
|
1013
|
+
console.error("[mcp] cc-agent spawn error:", err.message);
|
|
1014
|
+
clearTimeout(timeout);
|
|
1015
|
+
done(null);
|
|
1016
|
+
});
|
|
1017
|
+
proc.on("exit", () => { clearTimeout(timeout); done(null); });
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
893
1020
|
killSession(chatId, keepCrons = true) {
|
|
894
1021
|
const session = this.sessions.get(chatId);
|
|
895
1022
|
if (session) {
|