@emqo/claudebridge 0.2.2 → 0.3.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.
- package/dist/adapters/telegram.d.ts +4 -0
- package/dist/adapters/telegram.js +111 -10
- package/dist/cli.js +0 -0
- package/dist/core/agent.js +8 -3
- package/dist/core/i18n.js +2 -0
- package/dist/ctl.js +0 -0
- package/package.json +1 -1
|
@@ -12,12 +12,16 @@ export declare class TelegramAdapter implements Adapter {
|
|
|
12
12
|
private reminderTimer?;
|
|
13
13
|
private autoTimer?;
|
|
14
14
|
private autoRunning;
|
|
15
|
+
private pages;
|
|
16
|
+
private static PAGE_TTL;
|
|
15
17
|
constructor(engine: AgentEngine, store: Store, config: TelegramConfig, locale?: string);
|
|
16
18
|
private get api();
|
|
17
19
|
private call;
|
|
18
20
|
private reply;
|
|
19
21
|
private editMsg;
|
|
20
22
|
private handleUpdate;
|
|
23
|
+
private pageKeyboard;
|
|
24
|
+
private handlePageCallback;
|
|
21
25
|
private handlePrompt;
|
|
22
26
|
start(): Promise<void>;
|
|
23
27
|
stop(): void;
|
|
@@ -13,6 +13,8 @@ export class TelegramAdapter {
|
|
|
13
13
|
reminderTimer;
|
|
14
14
|
autoTimer;
|
|
15
15
|
autoRunning = false;
|
|
16
|
+
pages = new Map();
|
|
17
|
+
static PAGE_TTL = 30 * 60 * 1000; // 30 minutes
|
|
16
18
|
constructor(engine, store, config, locale = "en") {
|
|
17
19
|
this.engine = engine;
|
|
18
20
|
this.store = store;
|
|
@@ -35,10 +37,16 @@ export class TelegramAdapter {
|
|
|
35
37
|
});
|
|
36
38
|
clearTimeout(timer);
|
|
37
39
|
const json = await res.json();
|
|
40
|
+
if (!json.ok) {
|
|
41
|
+
console.error(`[telegram] API error ${method}:`, json.description || json);
|
|
42
|
+
const err = new Error(json.description || `Telegram API error: ${method}`);
|
|
43
|
+
err.apiError = true;
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
38
46
|
return json.result;
|
|
39
47
|
}
|
|
40
48
|
catch (err) {
|
|
41
|
-
if (i === 2)
|
|
49
|
+
if (err.apiError || i === 2)
|
|
42
50
|
throw err;
|
|
43
51
|
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
|
|
44
52
|
}
|
|
@@ -63,6 +71,10 @@ export class TelegramAdapter {
|
|
|
63
71
|
catch { }
|
|
64
72
|
}
|
|
65
73
|
async handleUpdate(update) {
|
|
74
|
+
if (update.callback_query) {
|
|
75
|
+
await this.handlePageCallback(update.callback_query);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
66
78
|
const msg = update.message;
|
|
67
79
|
if (!msg)
|
|
68
80
|
return;
|
|
@@ -162,6 +174,71 @@ export class TelegramAdapter {
|
|
|
162
174
|
if (text)
|
|
163
175
|
await this.handlePrompt(chatId, String(uid), text);
|
|
164
176
|
}
|
|
177
|
+
pageKeyboard(chatId, msgId, cur, total) {
|
|
178
|
+
const btns = [];
|
|
179
|
+
if (cur > 0)
|
|
180
|
+
btns.push({ text: "◀", callback_data: `p:${chatId}:${msgId}:${cur - 1}` });
|
|
181
|
+
btns.push({ text: `${cur + 1} / ${total}`, callback_data: "noop" });
|
|
182
|
+
if (cur < total - 1)
|
|
183
|
+
btns.push({ text: "▶", callback_data: `p:${chatId}:${msgId}:${cur + 1}` });
|
|
184
|
+
return { inline_keyboard: [btns] };
|
|
185
|
+
}
|
|
186
|
+
async handlePageCallback(cb) {
|
|
187
|
+
const data = cb.data || "";
|
|
188
|
+
const cbId = cb.id;
|
|
189
|
+
// Always answer callback to remove loading spinner
|
|
190
|
+
const answer = (text) => this.call("answerCallbackQuery", { callback_query_id: cbId, ...(text ? { text, show_alert: false } : {}) });
|
|
191
|
+
if (data === "noop") {
|
|
192
|
+
await answer();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!data.startsWith("p:")) {
|
|
196
|
+
await answer();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const parts = data.split(":");
|
|
200
|
+
if (parts.length !== 4) {
|
|
201
|
+
await answer();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const chatId = Number(parts[1]);
|
|
205
|
+
const msgId = Number(parts[2]);
|
|
206
|
+
const page = Number(parts[3]);
|
|
207
|
+
const key = `${chatId}:${msgId}`;
|
|
208
|
+
const entry = this.pages.get(key);
|
|
209
|
+
if (!entry || Date.now() - entry.ts > TelegramAdapter.PAGE_TTL) {
|
|
210
|
+
this.pages.delete(key);
|
|
211
|
+
await answer(t(this.locale, "page_expired"));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (page < 0 || page >= entry.chunks.length) {
|
|
215
|
+
await answer();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const keyboard = this.pageKeyboard(chatId, msgId, page, entry.chunks.length);
|
|
219
|
+
try {
|
|
220
|
+
await this.call("editMessageText", {
|
|
221
|
+
chat_id: chatId,
|
|
222
|
+
message_id: msgId,
|
|
223
|
+
text: entry.chunks[page],
|
|
224
|
+
parse_mode: "MarkdownV2",
|
|
225
|
+
reply_markup: keyboard,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// MarkdownV2 failed, fallback to raw text
|
|
230
|
+
try {
|
|
231
|
+
await this.call("editMessageText", {
|
|
232
|
+
chat_id: chatId,
|
|
233
|
+
message_id: msgId,
|
|
234
|
+
text: entry.raw[page],
|
|
235
|
+
reply_markup: keyboard,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
catch { }
|
|
239
|
+
}
|
|
240
|
+
await answer();
|
|
241
|
+
}
|
|
165
242
|
async handlePrompt(chatId, uid, text) {
|
|
166
243
|
if (this.engine.isLocked(uid)) {
|
|
167
244
|
await this.reply(chatId, t(this.locale, "still_processing"));
|
|
@@ -183,19 +260,43 @@ export class TelegramAdapter {
|
|
|
183
260
|
console.log(`[telegram] claude done for ${uid}, cost=$${res.cost?.toFixed(4)}`);
|
|
184
261
|
const maxLen = this.config.chunk_size || 4000;
|
|
185
262
|
const md = toTelegramMarkdown(res.text);
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
263
|
+
const mdChunks = chunkText(md, maxLen);
|
|
264
|
+
const rawChunks = chunkText(res.text, maxLen);
|
|
265
|
+
if (mdChunks.length <= 1) {
|
|
266
|
+
// Single page — no pagination needed
|
|
267
|
+
try {
|
|
268
|
+
await this.editMsg(chatId, msgId, mdChunks[0], "MarkdownV2");
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
await this.editMsg(chatId, msgId, res.text);
|
|
272
|
+
}
|
|
192
273
|
}
|
|
193
|
-
|
|
274
|
+
else {
|
|
275
|
+
// Multi-page — store pages and show inline keyboard
|
|
276
|
+
const key = `${chatId}:${msgId}`;
|
|
277
|
+
this.pages.set(key, { chunks: mdChunks, raw: rawChunks, ts: Date.now() });
|
|
278
|
+
setTimeout(() => this.pages.delete(key), TelegramAdapter.PAGE_TTL);
|
|
279
|
+
const keyboard = this.pageKeyboard(chatId, msgId, 0, mdChunks.length);
|
|
194
280
|
try {
|
|
195
|
-
await this.
|
|
281
|
+
await this.call("editMessageText", {
|
|
282
|
+
chat_id: chatId,
|
|
283
|
+
message_id: msgId,
|
|
284
|
+
text: mdChunks[0],
|
|
285
|
+
parse_mode: "MarkdownV2",
|
|
286
|
+
reply_markup: keyboard,
|
|
287
|
+
});
|
|
196
288
|
}
|
|
197
289
|
catch {
|
|
198
|
-
|
|
290
|
+
// MarkdownV2 failed, fallback to raw text
|
|
291
|
+
try {
|
|
292
|
+
await this.call("editMessageText", {
|
|
293
|
+
chat_id: chatId,
|
|
294
|
+
message_id: msgId,
|
|
295
|
+
text: rawChunks[0],
|
|
296
|
+
reply_markup: keyboard,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
catch { }
|
|
199
300
|
}
|
|
200
301
|
}
|
|
201
302
|
}
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/core/agent.js
CHANGED
|
@@ -129,7 +129,7 @@ export class AgentEngine {
|
|
|
129
129
|
let buffer = "";
|
|
130
130
|
child.stdout.on("data", (data) => {
|
|
131
131
|
const chunk = data.toString();
|
|
132
|
-
console.log(`[agent] stdout chunk: ${chunk.slice(0,
|
|
132
|
+
console.log(`[agent] stdout chunk: ${chunk.slice(0, 200)}`);
|
|
133
133
|
buffer += chunk;
|
|
134
134
|
const lines = buffer.split("\n");
|
|
135
135
|
buffer = lines.pop() || "";
|
|
@@ -155,6 +155,9 @@ export class AgentEngine {
|
|
|
155
155
|
fullText = msg.result;
|
|
156
156
|
if (msg.total_cost_usd)
|
|
157
157
|
cost = msg.total_cost_usd;
|
|
158
|
+
if (msg.is_error) {
|
|
159
|
+
console.error(`[agent] claude error result: subtype=${msg.subtype} message=${JSON.stringify(msg.error_message || msg.message || "unknown").slice(0, 500)}`);
|
|
160
|
+
}
|
|
158
161
|
}
|
|
159
162
|
}
|
|
160
163
|
catch { }
|
|
@@ -175,12 +178,14 @@ export class AgentEngine {
|
|
|
175
178
|
resolve({ text: fullText.trim() || "(timed out)", sessionId: newSessionId, cost });
|
|
176
179
|
return;
|
|
177
180
|
}
|
|
178
|
-
if (newSessionId)
|
|
179
|
-
this.store.setSession(userId, newSessionId, platform);
|
|
180
181
|
if (code === 0 || fullText.trim()) {
|
|
182
|
+
if (newSessionId)
|
|
183
|
+
this.store.setSession(userId, newSessionId, platform);
|
|
181
184
|
resolve({ text: fullText.trim() || "(no response)", sessionId: newSessionId, cost });
|
|
182
185
|
}
|
|
183
186
|
else {
|
|
187
|
+
// Don't save session on failure — stale session would lock the user in a failure loop
|
|
188
|
+
this.store.clearSession(userId);
|
|
184
189
|
reject(new Error(`claude exited ${code}: ${stderr.slice(0, 500)}`));
|
|
185
190
|
}
|
|
186
191
|
});
|
package/dist/core/i18n.js
CHANGED
|
@@ -13,6 +13,7 @@ const messages = {
|
|
|
13
13
|
auto_starting: "🤖 Auto task #{id} starting:\n{desc}",
|
|
14
14
|
auto_done: "✅ Auto task #{id} done (cost: ${cost}):",
|
|
15
15
|
auto_failed: "❌ Auto task #{id} failed: {err}",
|
|
16
|
+
page_expired: "Page expired. Please resend your question.",
|
|
16
17
|
},
|
|
17
18
|
zh: {
|
|
18
19
|
help: "ClaudeBridge 就绪。\n\n管理命令:\n/new - 清除会话\n/usage - 你的用量\n/allusage - 所有用量\n/history - 最近对话\n/model - 端点信息\n/reload - 重载配置\n/help - 显示帮助\n\n直接对话即可管理记忆、任务、提醒等 — Claude 会自动处理。",
|
|
@@ -28,6 +29,7 @@ const messages = {
|
|
|
28
29
|
auto_starting: "🤖 自动任务 #{id} 开始执行:\n{desc}",
|
|
29
30
|
auto_done: "✅ 自动任务 #{id} 完成(花费:${cost}):",
|
|
30
31
|
auto_failed: "❌ 自动任务 #{id} 失败:{err}",
|
|
32
|
+
page_expired: "页面已过期,请重新发送问题。",
|
|
31
33
|
},
|
|
32
34
|
};
|
|
33
35
|
const commandDescriptions = {
|
package/dist/ctl.js
CHANGED
|
File without changes
|