@gonzih/cc-tg 0.9.23 → 0.9.24
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/notifier.d.ts +1 -0
- package/dist/notifier.js +66 -0
- package/package.json +1 -1
package/dist/notifier.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Channels:
|
|
5
5
|
* cca:notify:{namespace} — job completion notifications from cc-agent → forward to Telegram
|
|
6
6
|
* cca:chat:incoming:{namespace} — messages from the web UI → echo to Telegram + feed into Claude session
|
|
7
|
+
* cca:chat:outgoing:* — meta-agent stdout lines (source=claude) → buffer+debounce → Telegram
|
|
7
8
|
*
|
|
8
9
|
* All messages (Telegram incoming, Claude responses) are also written to:
|
|
9
10
|
* cca:chat:log:{namespace} — LPUSH + LTRIM 0 499 (last 500 messages)
|
package/dist/notifier.js
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* Channels:
|
|
5
5
|
* cca:notify:{namespace} — job completion notifications from cc-agent → forward to Telegram
|
|
6
6
|
* cca:chat:incoming:{namespace} — messages from the web UI → echo to Telegram + feed into Claude session
|
|
7
|
+
* cca:chat:outgoing:* — meta-agent stdout lines (source=claude) → buffer+debounce → Telegram
|
|
7
8
|
*
|
|
8
9
|
* All messages (Telegram incoming, Claude responses) are also written to:
|
|
9
10
|
* cca:chat:log:{namespace} — LPUSH + LTRIM 0 499 (last 500 messages)
|
|
10
11
|
* cca:chat:outgoing:{namespace} — PUBLISH for web UI to consume
|
|
11
12
|
*/
|
|
13
|
+
import { splitLongMessage } from "./formatter.js";
|
|
12
14
|
function log(level, ...args) {
|
|
13
15
|
const fn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
|
|
14
16
|
fn("[notifier]", ...args);
|
|
@@ -30,6 +32,11 @@ function shortenModelName(model, driver) {
|
|
|
30
32
|
return model.slice(slashIdx + 1);
|
|
31
33
|
return model;
|
|
32
34
|
}
|
|
35
|
+
/** Strip ANSI escape sequences from a string before sending to Telegram. */
|
|
36
|
+
function stripAnsi(text) {
|
|
37
|
+
// eslint-disable-next-line no-control-regex
|
|
38
|
+
return text.replace(/\x1B\[[0-9;]*[mGKHF]/g, "");
|
|
39
|
+
}
|
|
33
40
|
/**
|
|
34
41
|
* Parse a notification payload and return the display text.
|
|
35
42
|
* Appends a [driver] or [driver:model] badge whenever the driver field is present.
|
|
@@ -124,6 +131,65 @@ export function startNotifier(bot, chatId, namespace, redis, handleUserMessage,
|
|
|
124
131
|
log("info", `subscribed to cca:chat:incoming:${namespace}`);
|
|
125
132
|
}
|
|
126
133
|
});
|
|
134
|
+
// cca:chat:outgoing:* — meta-agent stdout lines (source=claude) → buffer+debounce → Telegram
|
|
135
|
+
// Using psubscribe so we catch all namespaces (money-brain, isoc-nevada, etc.)
|
|
136
|
+
sub.psubscribe("cca:chat:outgoing:*", (err) => {
|
|
137
|
+
if (err) {
|
|
138
|
+
log("error", "psubscribe cca:chat:outgoing:* failed:", err.message);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
log("info", "psubscribed to cca:chat:outgoing:*");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// Per-namespace debounce buffer: accumulate streaming lines, flush after 1.5s silence
|
|
145
|
+
const metaAgentBuffers = new Map();
|
|
146
|
+
function flushMetaAgentBuffer(ns, targetChatId) {
|
|
147
|
+
const buf = metaAgentBuffers.get(ns);
|
|
148
|
+
if (!buf || !buf.text.trim())
|
|
149
|
+
return;
|
|
150
|
+
const text = stripAnsi(buf.text.trim());
|
|
151
|
+
buf.text = "";
|
|
152
|
+
buf.timer = null;
|
|
153
|
+
const chunks = splitLongMessage(text);
|
|
154
|
+
for (const chunk of chunks) {
|
|
155
|
+
bot.sendMessage(targetChatId, chunk).catch((err) => {
|
|
156
|
+
log("warn", `meta-agent flush sendMessage failed (ns=${ns}):`, err.message);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
sub.on("pmessage", (pattern, channel, message) => {
|
|
161
|
+
void pattern; // used only as a type guard
|
|
162
|
+
const ns = channel.replace("cca:chat:outgoing:", "");
|
|
163
|
+
let parsed = null;
|
|
164
|
+
try {
|
|
165
|
+
parsed = JSON.parse(message);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return; // non-JSON line — skip
|
|
169
|
+
}
|
|
170
|
+
// Only forward messages from the meta-agent (source=claude).
|
|
171
|
+
// cc-tg itself publishes to this channel with source "cc-tg"/"telegram"/"ui" — skip those.
|
|
172
|
+
if (parsed.source !== "claude")
|
|
173
|
+
return;
|
|
174
|
+
const content = parsed.content;
|
|
175
|
+
if (!content)
|
|
176
|
+
return;
|
|
177
|
+
const targetChatId = chatId ?? getActiveChatId?.();
|
|
178
|
+
if (targetChatId == null) {
|
|
179
|
+
log("warn", `meta-agent output: no chatId for namespace=${ns}, dropping line`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Accumulate into per-namespace buffer and (re-)arm debounce timer
|
|
183
|
+
let buf = metaAgentBuffers.get(ns);
|
|
184
|
+
if (!buf) {
|
|
185
|
+
buf = { text: "", timer: null };
|
|
186
|
+
metaAgentBuffers.set(ns, buf);
|
|
187
|
+
}
|
|
188
|
+
buf.text += (buf.text ? "\n" : "") + content;
|
|
189
|
+
if (buf.timer)
|
|
190
|
+
clearTimeout(buf.timer);
|
|
191
|
+
buf.timer = setTimeout(() => flushMetaAgentBuffer(ns, targetChatId), 1500);
|
|
192
|
+
});
|
|
127
193
|
// Poll the cca:notify:{namespace} LIST every 5 seconds.
|
|
128
194
|
// Jobs push to this list via RPUSH; pub/sub alone won't deliver those messages.
|
|
129
195
|
const notifyListKey = `cca:notify:${namespace}`;
|