@emqo/claudebridge 0.5.1 → 0.5.3

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.
@@ -242,6 +242,13 @@ export class DiscordAdapter {
242
242
  const res = this.maxParallel > 1
243
243
  ? await this.engine.runParallel(task.user_id, task.description, "discord", task.chat_id)
244
244
  : await this.engine.runStream(task.user_id, task.description, "discord", task.chat_id);
245
+ if (res.timedOut) {
246
+ this.store.markTaskResult(task.id, "failed");
247
+ if (res.text)
248
+ this.store.setTaskResult(task.id, res.text.slice(0, 10000));
249
+ await channel.send(t(this.locale, "auto_failed", { id: task.id, err: "timed out" }));
250
+ return;
251
+ }
245
252
  this.store.markTaskResult(task.id, "done");
246
253
  if (res.text)
247
254
  this.store.setTaskResult(task.id, res.text.slice(0, 10000));
@@ -396,14 +396,28 @@ export class TelegramAdapter {
396
396
  const res = this.maxParallel > 1
397
397
  ? await this.engine.runParallel(task.user_id, task.description, "telegram", task.chat_id)
398
398
  : await this.engine.runStream(task.user_id, task.description, "telegram", task.chat_id);
399
+ if (res.timedOut) {
400
+ this.store.markTaskResult(task.id, "failed");
401
+ if (res.text)
402
+ this.store.setTaskResult(task.id, res.text.slice(0, 10000));
403
+ await this.reply(chatId, t(this.locale, "auto_failed", { id: task.id, err: "timed out" }));
404
+ return;
405
+ }
399
406
  this.store.markTaskResult(task.id, "done");
400
407
  if (res.text)
401
408
  this.store.setTaskResult(task.id, res.text.slice(0, 10000));
402
409
  const maxLen = this.config.chunk_size || 4000;
403
- const chunks = chunkText(res.text || "(no output)", maxLen);
410
+ const rawChunks = chunkText(res.text || "(no output)", maxLen);
411
+ const mdChunks = chunkText(toTelegramMarkdown(res.text || "(no output)"), maxLen);
404
412
  await this.reply(chatId, t(this.locale, "auto_done", { id: task.id, cost: (res.cost || 0).toFixed(4) }));
405
- for (const c of chunks)
406
- await this.reply(chatId, c);
413
+ for (let i = 0; i < mdChunks.length; i++) {
414
+ try {
415
+ await this.call("sendMessage", { chat_id: chatId, text: mdChunks[i], parse_mode: "MarkdownV2" });
416
+ }
417
+ catch {
418
+ await this.reply(chatId, rawChunks[i] || mdChunks[i]);
419
+ }
420
+ }
407
421
  // Chain progress reporting
408
422
  if (task.parent_id) {
409
423
  const progress = this.store.getChainProgress(task.parent_id);
@@ -6,6 +6,7 @@ export interface AgentResponse {
6
6
  text: string;
7
7
  sessionId: string;
8
8
  cost?: number;
9
+ timedOut?: boolean;
9
10
  }
10
11
  export type StreamCallback = (chunk: string, full: string) => void | Promise<void>;
11
12
  export declare class AgentEngine {
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "child_process";
2
2
  import { mkdirSync } from "fs";
3
- import { join, resolve as pathResolve } from "path";
3
+ import { join } from "path";
4
4
  import { UserLock } from "./lock.js";
5
5
  import { AccessControl } from "./permissions.js";
6
6
  import { EndpointRotator } from "./keys.js";
@@ -146,11 +146,11 @@ export class AgentEngine {
146
146
  env.ANTHROPIC_API_KEY = ep.api_key;
147
147
  if (ep.base_url)
148
148
  env.ANTHROPIC_BASE_URL = ep.base_url;
149
- env.CLAUDEBRIDGE_DB = pathResolve("./data/claudebridge.db");
149
+ env.CLAUDEBRIDGE_DB = this.store.dbPath;
150
150
  const child = spawn("claude", args, { cwd, env, stdio: ["pipe", "pipe", "pipe"] });
151
151
  child.stdin.end();
152
152
  console.log(`[agent] spawned claude pid=${child.pid} cwd=${cwd} args=${args.join(" ")}`);
153
- const timeoutMs = (this.config.agent.timeout_seconds || 300) * 1000;
153
+ const timeoutMs = (this.config.agent.timeout_seconds || 600) * 1000;
154
154
  const timer = setTimeout(() => { try {
155
155
  child.kill("SIGTERM");
156
156
  }
@@ -205,9 +205,10 @@ export class AgentEngine {
205
205
  clearTimeout(timer);
206
206
  console.log(`[agent] claude exited code=${code} signal=${signal} fullText=${fullText.length}chars stderr=${stderr.slice(0, 200)}`);
207
207
  if (signal === "SIGTERM") {
208
+ console.warn(`[agent] claude timed out after ${timeoutMs / 1000}s`);
208
209
  if (newSessionId)
209
210
  this.store.setSession(userId, newSessionId, platform);
210
- resolve({ text: fullText.trim() || "(timed out)", sessionId: newSessionId, cost });
211
+ resolve({ text: fullText.trim() || "(timed out)", sessionId: newSessionId, cost, timedOut: true });
211
212
  return;
212
213
  }
213
214
  if (code === 0 || fullText.trim()) {
@@ -252,11 +253,11 @@ export class AgentEngine {
252
253
  env.ANTHROPIC_API_KEY = ep.api_key;
253
254
  if (ep.base_url)
254
255
  env.ANTHROPIC_BASE_URL = ep.base_url;
255
- env.CLAUDEBRIDGE_DB = pathResolve("./data/claudebridge.db");
256
+ env.CLAUDEBRIDGE_DB = this.store.dbPath;
256
257
  const child = spawn("claude", args, { cwd, env, stdio: ["pipe", "pipe", "pipe"] });
257
258
  child.stdin.end();
258
259
  console.log(`[agent] spawned claude (parallel) pid=${child.pid} cwd=${cwd}`);
259
- const timeoutMs = (this.config.agent.timeout_seconds || 300) * 1000;
260
+ const timeoutMs = (this.config.agent.timeout_seconds || 600) * 1000;
260
261
  const timer = setTimeout(() => { try {
261
262
  child.kill("SIGTERM");
262
263
  }
@@ -301,8 +302,12 @@ export class AgentEngine {
301
302
  child.on("close", (code, signal) => {
302
303
  clearTimeout(timer);
303
304
  console.log(`[agent] claude (parallel) exited code=${code} signal=${signal} text=${fullText.length}chars`);
304
- if (code === 0 || fullText.trim() || signal === "SIGTERM") {
305
- resolve({ text: fullText.trim() || (signal === "SIGTERM" ? "(timed out)" : "(no response)"), sessionId, cost });
305
+ if (signal === "SIGTERM") {
306
+ console.warn(`[agent] claude (parallel) timed out after ${timeoutMs / 1000}s`);
307
+ resolve({ text: fullText.trim() || "(timed out)", sessionId, cost, timedOut: true });
308
+ }
309
+ else if (code === 0 || fullText.trim()) {
310
+ resolve({ text: fullText.trim() || "(no response)", sessionId, cost });
306
311
  }
307
312
  else {
308
313
  reject(new Error(`claude exited ${code}: ${stderr.slice(0, 500)}`));
@@ -1,6 +1,7 @@
1
1
  export declare class Store {
2
2
  private db;
3
- constructor();
3
+ readonly dbPath: string;
4
+ constructor(dbPath?: string);
4
5
  getSession(userId: string): string | null;
5
6
  setSession(userId: string, sessionId: string, platform: string): void;
6
7
  clearSession(userId: string): void;
@@ -1,12 +1,14 @@
1
1
  import Database from "better-sqlite3";
2
2
  import { mkdirSync } from "fs";
3
- import { dirname } from "path";
4
- const DB_PATH = "./data/claudebridge.db";
3
+ import { dirname, resolve } from "path";
4
+ const DEFAULT_DB_PATH = "./data/claudebridge.db";
5
5
  export class Store {
6
6
  db;
7
- constructor() {
8
- mkdirSync(dirname(DB_PATH), { recursive: true });
9
- this.db = new Database(DB_PATH);
7
+ dbPath;
8
+ constructor(dbPath) {
9
+ this.dbPath = resolve(dbPath || DEFAULT_DB_PATH);
10
+ mkdirSync(dirname(this.dbPath), { recursive: true });
11
+ this.db = new Database(this.dbPath);
10
12
  this.db.pragma("journal_mode = WAL");
11
13
  this.db.exec(`
12
14
  CREATE TABLE IF NOT EXISTS sessions (
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { setDefaultAutoSelectFamily } from "net";
2
2
  setDefaultAutoSelectFamily(false);
3
3
  import { watch } from "fs";
4
+ import { join, dirname, resolve } from "path";
4
5
  import { loadConfig, reloadConfig } from "./core/config.js";
5
6
  import { Store } from "./core/store.js";
6
7
  import { AgentEngine } from "./core/agent.js";
@@ -11,7 +12,10 @@ async function main() {
11
12
  const _cfgIdx = process.argv.indexOf("--config");
12
13
  const _cfgPath = _cfgIdx !== -1 ? process.argv[_cfgIdx + 1] : undefined;
13
14
  let config = loadConfig(_cfgPath);
14
- const store = new Store();
15
+ // Derive DB path from config file directory (not CWD)
16
+ const configDir = _cfgPath ? dirname(resolve(_cfgPath)) : process.cwd();
17
+ const dbPath = join(configDir, "data", "claudebridge.db");
18
+ const store = new Store(dbPath);
15
19
  const engine = new AgentEngine(config, store);
16
20
  const adapters = [];
17
21
  let webhookServer = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emqo/claudebridge",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Bridge claude CLI to chat platforms (Telegram, Discord) with HITL approval, conditional branching, webhook triggers, parallel execution, and observability",
5
5
  "main": "dist/index.js",
6
6
  "bin": {