@gonzih/cc-tg 0.2.18 → 0.2.19

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 CHANGED
@@ -42,6 +42,7 @@ export declare class CcTgBot {
42
42
  private handleClearNpxCache;
43
43
  private handleRestart;
44
44
  private handleGetFile;
45
+ private callCcAgentTool;
45
46
  private killSession;
46
47
  stop(): void;
47
48
  }
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";
@@ -65,6 +65,28 @@ function formatCronCostFooter(usage) {
65
65
  const cost = computeCostUsd(usage);
66
66
  return `\nšŸ’° Cron cost: $${cost.toFixed(4)} (${formatTokens(usage.inputTokens)} in / ${formatTokens(usage.outputTokens)} out tokens)`;
67
67
  }
68
+ function formatAgentCostSummary(text) {
69
+ try {
70
+ const data = JSON.parse(text);
71
+ const totalCost = (data.total_cost_usd ?? data.total_cost ?? 0);
72
+ const totalJobs = (data.total_jobs ?? data.job_count ?? 0);
73
+ const byRepo = (data.by_repo ?? []);
74
+ const lines = [
75
+ "šŸ¤– Agent jobs (all time)",
76
+ `Total: $${totalCost.toFixed(2)} across ${totalJobs} jobs`,
77
+ ];
78
+ for (const entry of byRepo) {
79
+ const repo = (entry.repo ?? entry.repository ?? "unknown");
80
+ const cost = (entry.cost_usd ?? entry.cost ?? 0);
81
+ const jobs = (entry.job_count ?? entry.jobs ?? 0);
82
+ lines.push(` ${repo}: $${cost.toFixed(2)} (${jobs} jobs)`);
83
+ }
84
+ return lines.join("\n");
85
+ }
86
+ catch {
87
+ return `šŸ¤– Agent jobs (all time)\n${text}`;
88
+ }
89
+ }
68
90
  class CostStore {
69
91
  costs = new Map();
70
92
  storePath;
@@ -237,7 +259,17 @@ export class CcTgBot {
237
259
  // /cost — show session token usage and cost
238
260
  if (text === "/cost") {
239
261
  const cost = this.costStore.get(chatId);
240
- await this.bot.sendMessage(chatId, formatCostReport(cost));
262
+ let reply = formatCostReport(cost);
263
+ try {
264
+ const rawSummary = await this.callCcAgentTool("cost_summary");
265
+ if (rawSummary) {
266
+ reply += "\n\n" + formatAgentCostSummary(rawSummary);
267
+ }
268
+ }
269
+ catch (err) {
270
+ console.error("[cost] cc-agent cost_summary failed:", err.message);
271
+ }
272
+ await this.bot.sendMessage(chatId, reply);
241
273
  return;
242
274
  }
243
275
  const session = this.getOrCreateSession(chatId);
@@ -890,6 +922,77 @@ export class CcTgBot {
890
922
  }
891
923
  await this.bot.sendDocument(chatId, filePath);
892
924
  }
925
+ callCcAgentTool(toolName, args = {}) {
926
+ return new Promise((resolve) => {
927
+ let settled = false;
928
+ const done = (val) => {
929
+ if (!settled) {
930
+ settled = true;
931
+ resolve(val);
932
+ }
933
+ };
934
+ let proc;
935
+ try {
936
+ proc = spawn("npx", ["-y", "@gonzih/cc-agent@latest"], {
937
+ env: { ...process.env },
938
+ stdio: ["pipe", "pipe", "pipe"],
939
+ });
940
+ }
941
+ catch (err) {
942
+ console.error("[mcp] failed to spawn cc-agent:", err.message);
943
+ done(null);
944
+ return;
945
+ }
946
+ const timeout = setTimeout(() => {
947
+ console.warn("[mcp] cc-agent tool call timed out");
948
+ proc.kill();
949
+ done(null);
950
+ }, 30_000);
951
+ let buffer = "";
952
+ const sendMsg = (msg) => { proc.stdin.write(JSON.stringify(msg) + "\n"); };
953
+ sendMsg({
954
+ jsonrpc: "2.0", id: 1, method: "initialize",
955
+ params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "cc-tg", version: "1.0.0" } },
956
+ });
957
+ proc.stdout.on("data", (chunk) => {
958
+ buffer += chunk.toString();
959
+ const lines = buffer.split("\n");
960
+ buffer = lines.pop() ?? "";
961
+ for (const line of lines) {
962
+ if (!line.trim())
963
+ continue;
964
+ try {
965
+ const msg = JSON.parse(line);
966
+ if (msg.id === 1 && "result" in msg) {
967
+ sendMsg({ jsonrpc: "2.0", method: "notifications/initialized" });
968
+ sendMsg({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: toolName, arguments: args } });
969
+ }
970
+ else if (msg.id === 2) {
971
+ clearTimeout(timeout);
972
+ if (msg.error) {
973
+ console.error("[mcp] cost_summary error:", JSON.stringify(msg.error));
974
+ proc.kill();
975
+ done(null);
976
+ return;
977
+ }
978
+ const result = msg.result;
979
+ const content = result?.content;
980
+ const text = (content ?? []).filter((b) => b.type === "text").map((b) => b.text).join("");
981
+ proc.kill();
982
+ done(text || null);
983
+ }
984
+ }
985
+ catch { /* ignore non-JSON lines */ }
986
+ }
987
+ });
988
+ proc.on("error", (err) => {
989
+ console.error("[mcp] cc-agent spawn error:", err.message);
990
+ clearTimeout(timeout);
991
+ done(null);
992
+ });
993
+ proc.on("exit", () => { clearTimeout(timeout); done(null); });
994
+ });
995
+ }
893
996
  killSession(chatId, keepCrons = true) {
894
997
  const session = this.sessions.get(chatId);
895
998
  if (session) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-tg",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "description": "Claude Code Telegram bot — chat with Claude Code via Telegram",
5
5
  "type": "module",
6
6
  "bin": {