@emqo/claudebridge 0.6.3 → 0.8.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/base.d.ts +1 -0
- package/dist/adapters/discord.d.ts +1 -0
- package/dist/adapters/discord.js +24 -3
- package/dist/adapters/telegram.d.ts +1 -0
- package/dist/adapters/telegram.js +40 -7
- package/dist/core/agent.js +5 -0
- package/dist/core/config.js +1 -1
- package/dist/core/i18n.js +2 -0
- package/dist/core/keys.js +2 -0
- package/dist/core/lock.js +4 -1
- package/dist/core/store.js +11 -3
- package/dist/ctl.js +19 -29
- package/dist/index.js +12 -0
- package/package.json +1 -1
package/dist/adapters/base.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface MessageContext {
|
|
|
7
7
|
export interface Adapter {
|
|
8
8
|
start(): Promise<void>;
|
|
9
9
|
stop(): void;
|
|
10
|
+
reloadConfig?(config: any, locale: string): void;
|
|
10
11
|
}
|
|
11
12
|
/** Split long text into chunks respecting newlines, with code-block-aware balancing */
|
|
12
13
|
export declare function chunkText(text: string, maxLen: number): string[];
|
|
@@ -14,6 +14,7 @@ export declare class DiscordAdapter implements Adapter {
|
|
|
14
14
|
private activeAutoTasks;
|
|
15
15
|
private maxParallel;
|
|
16
16
|
constructor(engine: AgentEngine, store: Store, config: DiscordConfig, locale?: string);
|
|
17
|
+
reloadConfig(config: DiscordConfig, locale: string): void;
|
|
17
18
|
private setup;
|
|
18
19
|
private handlePrompt;
|
|
19
20
|
start(): Promise<void>;
|
package/dist/adapters/discord.js
CHANGED
|
@@ -31,6 +31,11 @@ export class DiscordAdapter {
|
|
|
31
31
|
});
|
|
32
32
|
this.setup();
|
|
33
33
|
}
|
|
34
|
+
reloadConfig(config, locale) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.locale = locale;
|
|
37
|
+
this.maxParallel = this.engine.getMaxParallel();
|
|
38
|
+
}
|
|
34
39
|
setup() {
|
|
35
40
|
this.client.on("messageCreate", async (msg) => {
|
|
36
41
|
if (msg.author.bot)
|
|
@@ -239,14 +244,21 @@ export class DiscordAdapter {
|
|
|
239
244
|
const channel = ch;
|
|
240
245
|
await channel.send(t(this.locale, "auto_starting", { id: task.id, desc: task.description }));
|
|
241
246
|
console.log(`[discord] auto-task #${task.id} for ${task.user_id}`);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
: await this.engine.runStream(task.user_id, task.description, "discord", task.chat_id, undefined, 0);
|
|
247
|
+
// Always use runParallel for auto-tasks: fresh session, no user session pollution
|
|
248
|
+
const res = await this.engine.runParallel(task.user_id, task.description, "discord", task.chat_id, undefined, 0);
|
|
245
249
|
if (res.timedOut) {
|
|
246
250
|
this.store.markTaskResult(task.id, "failed");
|
|
247
251
|
if (res.text)
|
|
248
252
|
this.store.setTaskResult(task.id, res.text.slice(0, 10000));
|
|
249
253
|
await channel.send(t(this.locale, "auto_failed", { id: task.id, err: "timed out" }));
|
|
254
|
+
const retryMatch = task.description.match(/\[retry (\d+)\/3\]/);
|
|
255
|
+
const retryCount = retryMatch ? parseInt(retryMatch[1]) : 0;
|
|
256
|
+
if (retryCount < 3) {
|
|
257
|
+
const retryDesc = retryCount === 0
|
|
258
|
+
? `[retry 1/3] Previous attempt of task #${task.id} timed out. Continue from where it left off: ${task.description}`
|
|
259
|
+
: task.description.replace(`[retry ${retryCount}/3]`, `[retry ${retryCount + 1}/3]`);
|
|
260
|
+
this.store.addTask(task.user_id, "discord", task.chat_id, retryDesc, undefined, true, task.parent_id || task.id, Date.now() + 120000);
|
|
261
|
+
}
|
|
250
262
|
return;
|
|
251
263
|
}
|
|
252
264
|
this.store.markTaskResult(task.id, "done");
|
|
@@ -267,6 +279,15 @@ export class DiscordAdapter {
|
|
|
267
279
|
catch (err) {
|
|
268
280
|
this.store.markTaskResult(task.id, "failed");
|
|
269
281
|
console.error(`[discord] auto-task #${task.id} failed:`, err);
|
|
282
|
+
// Self-healing: auto-retry failed tasks (max 3 retries)
|
|
283
|
+
const retryMatch = task.description.match(/\[retry (\d+)\/3\]/);
|
|
284
|
+
const retryCount = retryMatch ? parseInt(retryMatch[1]) : 0;
|
|
285
|
+
if (retryCount < 3) {
|
|
286
|
+
const retryDesc = retryCount === 0
|
|
287
|
+
? `[retry 1/3] Previous attempt of task #${task.id} failed (${(err.message || "unknown").slice(0, 100)}). Analyze the failure, fix the issue, then: ${task.description}`
|
|
288
|
+
: task.description.replace(`[retry ${retryCount}/3]`, `[retry ${retryCount + 1}/3]`);
|
|
289
|
+
this.store.addTask(task.user_id, "discord", task.chat_id, retryDesc, undefined, true, task.parent_id || task.id, Date.now() + 120000);
|
|
290
|
+
}
|
|
270
291
|
try {
|
|
271
292
|
const ch = await this.client.channels.fetch(task.chat_id);
|
|
272
293
|
if (ch?.isTextBased() && "send" in ch) {
|
|
@@ -17,6 +17,7 @@ export declare class TelegramAdapter implements Adapter {
|
|
|
17
17
|
private pages;
|
|
18
18
|
private static PAGE_TTL;
|
|
19
19
|
constructor(engine: AgentEngine, store: Store, config: TelegramConfig, locale?: string);
|
|
20
|
+
reloadConfig(config: TelegramConfig, locale: string): void;
|
|
20
21
|
private get api();
|
|
21
22
|
private call;
|
|
22
23
|
private reply;
|
|
@@ -23,6 +23,11 @@ export class TelegramAdapter {
|
|
|
23
23
|
this.config = config;
|
|
24
24
|
this.locale = locale;
|
|
25
25
|
}
|
|
26
|
+
reloadConfig(config, locale) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.locale = locale;
|
|
29
|
+
this.maxParallel = this.engine.getMaxParallel();
|
|
30
|
+
}
|
|
26
31
|
get api() {
|
|
27
32
|
return `https://api.telegram.org/bot${this.config.token}`;
|
|
28
33
|
}
|
|
@@ -286,6 +291,11 @@ export class TelegramAdapter {
|
|
|
286
291
|
else {
|
|
287
292
|
// Multi-page — store pages and show inline keyboard
|
|
288
293
|
const key = `${chatId}:${msgId}`;
|
|
294
|
+
// Evict oldest if too many pages cached
|
|
295
|
+
if (this.pages.size >= 50) {
|
|
296
|
+
const oldest = this.pages.keys().next().value;
|
|
297
|
+
this.pages.delete(oldest);
|
|
298
|
+
}
|
|
289
299
|
this.pages.set(key, { chunks: mdChunks, raw: rawChunks, ts: Date.now() });
|
|
290
300
|
setTimeout(() => this.pages.delete(key), TelegramAdapter.PAGE_TTL);
|
|
291
301
|
const keyboard = this.pageKeyboard(chatId, msgId, 0, mdChunks.length);
|
|
@@ -325,25 +335,29 @@ export class TelegramAdapter {
|
|
|
325
335
|
this.autoTimer = setInterval(() => this.processAutoTasks(), 60000);
|
|
326
336
|
this.approvalTimer = setInterval(() => this.checkApprovals(), 15000);
|
|
327
337
|
await this.registerCommands();
|
|
338
|
+
let pollBackoff = 0;
|
|
328
339
|
while (this.running) {
|
|
329
340
|
try {
|
|
330
341
|
const ctrl = new AbortController();
|
|
331
|
-
const timer = setTimeout(() => ctrl.abort(),
|
|
332
|
-
const res = await fetch(`${this.api}/getUpdates?offset=${this.offset}&timeout=
|
|
342
|
+
const timer = setTimeout(() => ctrl.abort(), 30000);
|
|
343
|
+
const res = await fetch(`${this.api}/getUpdates?offset=${this.offset}&timeout=10`, { signal: ctrl.signal });
|
|
333
344
|
clearTimeout(timer);
|
|
334
345
|
const json = await res.json();
|
|
335
346
|
if (!json.ok) {
|
|
336
347
|
console.error("[telegram] poll error:", json);
|
|
337
348
|
continue;
|
|
338
349
|
}
|
|
350
|
+
pollBackoff = 0; // reset on success
|
|
339
351
|
for (const update of json.result) {
|
|
340
352
|
this.offset = update.update_id + 1;
|
|
341
353
|
this.handleUpdate(update).catch(e => console.error("[telegram] handler error:", e));
|
|
342
354
|
}
|
|
343
355
|
}
|
|
344
356
|
catch (err) {
|
|
345
|
-
|
|
346
|
-
|
|
357
|
+
pollBackoff = Math.min(pollBackoff + 1, 6);
|
|
358
|
+
const delay = Math.min(3000 * Math.pow(2, pollBackoff), 120000);
|
|
359
|
+
console.warn(`[telegram] poll error (retry in ${delay / 1000}s): ${err.cause?.code || err.message || "unknown"}`);
|
|
360
|
+
await new Promise(r => setTimeout(r, delay));
|
|
347
361
|
}
|
|
348
362
|
}
|
|
349
363
|
}
|
|
@@ -393,14 +407,23 @@ export class TelegramAdapter {
|
|
|
393
407
|
await this.reply(chatId, t(this.locale, "auto_starting", { id: task.id, desc: task.description }));
|
|
394
408
|
try {
|
|
395
409
|
console.log(`[telegram] auto-task #${task.id} for ${task.user_id}`);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
: await this.engine.runStream(task.user_id, task.description, "telegram", task.chat_id, undefined, 0);
|
|
410
|
+
// Always use runParallel for auto-tasks: fresh session, no user session pollution
|
|
411
|
+
const res = await this.engine.runParallel(task.user_id, task.description, "telegram", task.chat_id, undefined, 0);
|
|
399
412
|
if (res.timedOut) {
|
|
400
413
|
this.store.markTaskResult(task.id, "failed");
|
|
401
414
|
if (res.text)
|
|
402
415
|
this.store.setTaskResult(task.id, res.text.slice(0, 10000));
|
|
403
416
|
await this.reply(chatId, t(this.locale, "auto_failed", { id: task.id, err: "timed out" }));
|
|
417
|
+
// Self-healing: auto-retry timed out tasks
|
|
418
|
+
const retryMatch = task.description.match(/\[retry (\d+)\/3\]/);
|
|
419
|
+
const retryCount = retryMatch ? parseInt(retryMatch[1]) : 0;
|
|
420
|
+
if (retryCount < 3) {
|
|
421
|
+
const retryDesc = retryCount === 0
|
|
422
|
+
? `[retry 1/3] Previous attempt of task #${task.id} timed out. Continue from where it left off: ${task.description}`
|
|
423
|
+
: task.description.replace(`[retry ${retryCount}/3]`, `[retry ${retryCount + 1}/3]`);
|
|
424
|
+
const retryId = this.store.addTask(task.user_id, "telegram", task.chat_id, retryDesc, undefined, true, task.parent_id || task.id, Date.now() + 120000);
|
|
425
|
+
await this.reply(chatId, t(this.locale, "auto_retry", { id: retryId, attempt: retryCount + 1, parent: task.id }));
|
|
426
|
+
}
|
|
404
427
|
return;
|
|
405
428
|
}
|
|
406
429
|
this.store.markTaskResult(task.id, "done");
|
|
@@ -428,6 +451,16 @@ export class TelegramAdapter {
|
|
|
428
451
|
catch (err) {
|
|
429
452
|
this.store.markTaskResult(task.id, "failed");
|
|
430
453
|
await this.reply(chatId, t(this.locale, "auto_failed", { id: task.id, err: err.message || "unknown" }));
|
|
454
|
+
// Self-healing: auto-retry failed tasks (max 3 retries)
|
|
455
|
+
const retryMatch = task.description.match(/\[retry (\d+)\/3\]/);
|
|
456
|
+
const retryCount = retryMatch ? parseInt(retryMatch[1]) : 0;
|
|
457
|
+
if (retryCount < 3) {
|
|
458
|
+
const retryDesc = retryCount === 0
|
|
459
|
+
? `[retry 1/3] Previous attempt of task #${task.id} failed (${(err.message || "unknown").slice(0, 100)}). Analyze the failure, fix the issue, then: ${task.description}`
|
|
460
|
+
: task.description.replace(`[retry ${retryCount}/3]`, `[retry ${retryCount + 1}/3]`);
|
|
461
|
+
const retryId = this.store.addTask(task.user_id, "telegram", task.chat_id, retryDesc, undefined, true, task.parent_id || task.id, Date.now() + 120000);
|
|
462
|
+
await this.reply(chatId, t(this.locale, "auto_retry", { id: retryId, attempt: retryCount + 1, parent: task.id }));
|
|
463
|
+
}
|
|
431
464
|
}
|
|
432
465
|
}
|
|
433
466
|
async checkApprovals() {
|
package/dist/core/agent.js
CHANGED
|
@@ -333,6 +333,10 @@ export class AgentEngine {
|
|
|
333
333
|
args.push("--model", ep.model);
|
|
334
334
|
const child = spawn("claude", args, { env, stdio: ["pipe", "pipe", "pipe"] });
|
|
335
335
|
child.stdin.end();
|
|
336
|
+
const killTimer = setTimeout(() => { try {
|
|
337
|
+
child.kill("SIGTERM");
|
|
338
|
+
}
|
|
339
|
+
catch { } }, 60000);
|
|
336
340
|
console.log(`[agent] auto-summary spawned pid=${child.pid} for ${userId}`);
|
|
337
341
|
let result = "";
|
|
338
342
|
let cost = 0;
|
|
@@ -359,6 +363,7 @@ export class AgentEngine {
|
|
|
359
363
|
});
|
|
360
364
|
child.stderr.on("data", (data) => { stderr += data.toString(); });
|
|
361
365
|
child.on("close", (code) => {
|
|
366
|
+
clearTimeout(killTimer);
|
|
362
367
|
if (code !== 0) {
|
|
363
368
|
console.warn(`[agent] auto-summary failed code=${code} stderr=${stderr.slice(0, 200)} for ${userId}`);
|
|
364
369
|
}
|
package/dist/core/config.js
CHANGED
|
@@ -10,7 +10,7 @@ export function loadConfig(path) {
|
|
|
10
10
|
endpoints: raw.endpoints || [],
|
|
11
11
|
agent: {
|
|
12
12
|
...raw.agent,
|
|
13
|
-
timeout_seconds: raw.agent?.timeout_seconds ??
|
|
13
|
+
timeout_seconds: raw.agent?.timeout_seconds ?? 0,
|
|
14
14
|
max_parallel: raw.agent?.max_parallel ?? 1,
|
|
15
15
|
memory: { enabled: true, auto_summary: true, max_memories: 50, ...raw.agent?.memory },
|
|
16
16
|
skill: { enabled: true, ...raw.agent?.skill },
|
package/dist/core/i18n.js
CHANGED
|
@@ -14,6 +14,7 @@ const messages = {
|
|
|
14
14
|
auto_scheduled: "Auto task #{id} scheduled (executes in {minutes} min):\n{desc}",
|
|
15
15
|
auto_done: "Auto task #{id} done (cost: ${cost}):",
|
|
16
16
|
auto_failed: "Auto task #{id} failed: {err}",
|
|
17
|
+
auto_retry: "Self-healing: auto task #{parent} failed, retry #{attempt}/3 queued as task #{id} (in 2min)",
|
|
17
18
|
page_expired: "Page expired. Please resend your question.",
|
|
18
19
|
approval_request: "Approval needed for auto task #{id}:\n{desc}",
|
|
19
20
|
approval_approved: "Auto task #{id} approved -- queued for execution.",
|
|
@@ -38,6 +39,7 @@ const messages = {
|
|
|
38
39
|
auto_scheduled: "自动任务 #{id} 已排程({minutes} 分钟后执行):\n{desc}",
|
|
39
40
|
auto_done: "自动任务 #{id} 完成(花费:${cost}):",
|
|
40
41
|
auto_failed: "自动任务 #{id} 失败:{err}",
|
|
42
|
+
auto_retry: "自愈机制:自动任务 #{parent} 失败,重试 #{attempt}/3 已排队为任务 #{id}(2分钟后执行)",
|
|
41
43
|
page_expired: "页面已过期,请重新发送问题。",
|
|
42
44
|
approval_request: "自动任务 #{id} 需要审批:\n{desc}",
|
|
43
45
|
approval_approved: "自动任务 #{id} 已批准 -- 已加入执行队列。",
|
package/dist/core/keys.js
CHANGED
|
@@ -8,6 +8,8 @@ export class EndpointRotator {
|
|
|
8
8
|
this.endpoints = endpoints.filter(e => e.api_key);
|
|
9
9
|
}
|
|
10
10
|
next() {
|
|
11
|
+
if (!this.endpoints.length)
|
|
12
|
+
throw new Error("No endpoints configured");
|
|
11
13
|
const now = Date.now();
|
|
12
14
|
const len = this.endpoints.length;
|
|
13
15
|
for (let i = 0; i < len; i++) {
|
package/dist/core/lock.js
CHANGED
|
@@ -43,11 +43,14 @@ export class UserLock {
|
|
|
43
43
|
}
|
|
44
44
|
async _acquireRedis(userId) {
|
|
45
45
|
const key = this.prefix + userId;
|
|
46
|
-
//
|
|
46
|
+
const maxWait = this.ttl * 1000 + 5000; // TTL + 5s grace
|
|
47
|
+
const start = Date.now();
|
|
47
48
|
while (true) {
|
|
48
49
|
const ok = await this.redis.set(key, "1", "EX", this.ttl, "NX");
|
|
49
50
|
if (ok)
|
|
50
51
|
break;
|
|
52
|
+
if (Date.now() - start > maxWait)
|
|
53
|
+
throw new Error(`Lock timeout for user ${userId}`);
|
|
51
54
|
await new Promise((r) => setTimeout(r, 500));
|
|
52
55
|
}
|
|
53
56
|
return async () => {
|
package/dist/core/store.js
CHANGED
|
@@ -70,10 +70,18 @@ export class Store {
|
|
|
70
70
|
catch { }
|
|
71
71
|
this.db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id)");
|
|
72
72
|
// Startup recovery: reset orphaned 'running' tasks back to 'auto' so they get re-executed
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
const orphaned = this.db.prepare("SELECT id, description FROM tasks WHERE status = 'running'").all();
|
|
74
|
+
for (const t of orphaned) {
|
|
75
|
+
const desc = t.description.startsWith("[recovered]") ? t.description : `[recovered] Check current state before making changes — previous attempt was interrupted. Original task: ${t.description}`;
|
|
76
|
+
this.db.prepare("UPDATE tasks SET status = 'auto', description = ? WHERE id = ?").run(desc, t.id);
|
|
76
77
|
}
|
|
78
|
+
if (orphaned.length > 0) {
|
|
79
|
+
console.log(`[store] recovered ${orphaned.length} orphaned running task(s) back to auto queue`);
|
|
80
|
+
}
|
|
81
|
+
// Startup cleanup: prune history/usage older than 30 days
|
|
82
|
+
const cutoff = Date.now() - 30 * 86400000;
|
|
83
|
+
this.db.prepare("DELETE FROM history WHERE created_at < ?").run(cutoff);
|
|
84
|
+
this.db.prepare("DELETE FROM usage WHERE created_at < ?").run(cutoff);
|
|
77
85
|
}
|
|
78
86
|
// --- sessions ---
|
|
79
87
|
getSession(userId) {
|
package/dist/ctl.js
CHANGED
|
@@ -16,6 +16,17 @@ function fail(msg) {
|
|
|
16
16
|
console.error(msg);
|
|
17
17
|
process.exit(1);
|
|
18
18
|
}
|
|
19
|
+
function extractFlag(parts, flag) {
|
|
20
|
+
// Search from end to avoid matching flag text inside description
|
|
21
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
22
|
+
if (parts[i] === flag) {
|
|
23
|
+
const val = parts[i + 1];
|
|
24
|
+
parts.splice(i, 2);
|
|
25
|
+
return val;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
19
30
|
if (category === "memory") {
|
|
20
31
|
if (action === "add") {
|
|
21
32
|
const [userId, ...contentParts] = rest;
|
|
@@ -102,21 +113,10 @@ else if (category === "auto") {
|
|
|
102
113
|
const [userId, platform, chatId, ...descParts] = rest;
|
|
103
114
|
if (!userId || !platform || !chatId || !descParts.length)
|
|
104
115
|
fail("Usage: auto add <user_id> <platform> <chat_id> <description> [--parent <id>]");
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
parentId = parseInt(descParts[parentIdx + 1]);
|
|
110
|
-
descParts.splice(parentIdx, 2);
|
|
111
|
-
}
|
|
112
|
-
// Parse optional --delay flag
|
|
113
|
-
let scheduledAt = null;
|
|
114
|
-
const delayIdx = descParts.indexOf("--delay");
|
|
115
|
-
if (delayIdx !== -1 && descParts[delayIdx + 1]) {
|
|
116
|
-
const delayMin = parseInt(descParts[delayIdx + 1]);
|
|
117
|
-
scheduledAt = Date.now() + delayMin * 60000;
|
|
118
|
-
descParts.splice(delayIdx, 2);
|
|
119
|
-
}
|
|
116
|
+
const parentRaw = extractFlag(descParts, "--parent");
|
|
117
|
+
const parentId = parentRaw ? parseInt(parentRaw) : null;
|
|
118
|
+
const delayRaw = extractFlag(descParts, "--delay");
|
|
119
|
+
const scheduledAt = delayRaw ? Date.now() + parseInt(delayRaw) * 60000 : null;
|
|
120
120
|
const desc = descParts.join(" ");
|
|
121
121
|
const r = db.prepare("INSERT INTO tasks (user_id, platform, chat_id, description, status, parent_id, scheduled_at, created_at) VALUES (?, ?, ?, ?, 'auto', ?, ?, ?)").run(userId, platform, chatId, desc, parentId, scheduledAt, Date.now());
|
|
122
122
|
output({ ok: true, id: Number(r.lastInsertRowid), scheduled_at: scheduledAt, message: scheduledAt ? `Auto task scheduled (in ${Math.ceil((scheduledAt - Date.now()) / 60000)} min)` : "Auto task queued" });
|
|
@@ -125,20 +125,10 @@ else if (category === "auto") {
|
|
|
125
125
|
const [userId, platform, chatId, ...descParts] = rest;
|
|
126
126
|
if (!userId || !platform || !chatId || !descParts.length)
|
|
127
127
|
fail("Usage: auto add-approval <user_id> <platform> <chat_id> <description> [--parent <id>] [--delay <minutes>]");
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
descParts.splice(parentIdx, 2);
|
|
133
|
-
}
|
|
134
|
-
// Parse optional --delay flag
|
|
135
|
-
let scheduledAt = null;
|
|
136
|
-
const delayIdx = descParts.indexOf("--delay");
|
|
137
|
-
if (delayIdx !== -1 && descParts[delayIdx + 1]) {
|
|
138
|
-
const delayMin = parseInt(descParts[delayIdx + 1]);
|
|
139
|
-
scheduledAt = Date.now() + delayMin * 60000;
|
|
140
|
-
descParts.splice(delayIdx, 2);
|
|
141
|
-
}
|
|
128
|
+
const parentRaw = extractFlag(descParts, "--parent");
|
|
129
|
+
const parentId = parentRaw ? parseInt(parentRaw) : null;
|
|
130
|
+
const delayRaw = extractFlag(descParts, "--delay");
|
|
131
|
+
const scheduledAt = delayRaw ? Date.now() + parseInt(delayRaw) * 60000 : null;
|
|
142
132
|
const desc = descParts.join(" ");
|
|
143
133
|
const r = db.prepare("INSERT INTO tasks (user_id, platform, chat_id, description, status, parent_id, scheduled_at, created_at) VALUES (?, ?, ?, ?, 'approval_pending', ?, ?, ?)").run(userId, platform, chatId, desc, parentId, scheduledAt, Date.now());
|
|
144
134
|
output({ ok: true, id: Number(r.lastInsertRowid), scheduled_at: scheduledAt, message: scheduledAt ? `Auto task queued for approval (scheduled in ${Math.ceil((scheduledAt - Date.now()) / 60000)} min)` : "Auto task queued for approval" });
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,12 @@ async function main() {
|
|
|
57
57
|
try {
|
|
58
58
|
config = reloadConfig();
|
|
59
59
|
engine.reloadConfig(config);
|
|
60
|
+
for (const a of adapters) {
|
|
61
|
+
if ('reloadConfig' in a && typeof a.reloadConfig === 'function') {
|
|
62
|
+
const plat = a.constructor.name === 'TelegramAdapter' ? config.platforms.telegram : config.platforms.discord;
|
|
63
|
+
a.reloadConfig(plat, config.locale);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
60
66
|
console.log("[claudebridge] config reloaded (SIGHUP)");
|
|
61
67
|
}
|
|
62
68
|
catch (err) {
|
|
@@ -72,6 +78,12 @@ async function main() {
|
|
|
72
78
|
try {
|
|
73
79
|
config = reloadConfig();
|
|
74
80
|
engine.reloadConfig(config);
|
|
81
|
+
for (const a of adapters) {
|
|
82
|
+
if ('reloadConfig' in a && typeof a.reloadConfig === 'function') {
|
|
83
|
+
const plat = a.constructor.name === 'TelegramAdapter' ? config.platforms.telegram : config.platforms.discord;
|
|
84
|
+
a.reloadConfig(plat, config.locale);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
75
87
|
console.log("[claudebridge] config reloaded");
|
|
76
88
|
}
|
|
77
89
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emqo/claudebridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Bridge claude CLI to chat platforms (Telegram, Discord) with scheduled auto-tasks, autonomous project management, HITL approval, conditional branching, webhook triggers, parallel execution, and observability",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|