@emqo/claudebridge 0.5.0 → 0.5.2

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/README.md CHANGED
@@ -32,11 +32,11 @@ Instead of hardcoded commands, ClaudeBridge injects a **skill document** into Cl
32
32
 
33
33
  ### v0.5.0: Agent Gateway Features
34
34
 
35
- - **🔒 Human-in-the-Loop (HITL)**: Critical tasks require user approval via inline buttons (Telegram) or commands (Discord) before execution
36
- - **🔀 Conditional Branching**: Scout pattern — task 1 analyzes, saves results, dynamically creates follow-up tasks with `--parent` linking
37
- - **🌐 Webhook Triggers**: HTTP API + GitHub webhooks + cron scheduler — trigger auto-tasks from external systems
38
- - **⚡ Parallel Execution**: Multiple `claude` instances running simultaneously (`max_parallel` config)
39
- - **📊 Observability**: `/status` command shows task queue, chain progress, and execution stats
35
+ - **Human-in-the-Loop (HITL)**: Critical tasks require user approval via inline buttons (Telegram) or commands (Discord) before execution
36
+ - **Conditional Branching**: Scout pattern — task 1 analyzes, saves results, dynamically creates follow-up tasks with `--parent` linking
37
+ - **Webhook Triggers**: HTTP API + GitHub webhooks + cron scheduler — trigger auto-tasks from external systems
38
+ - **Parallel Execution**: Multiple `claude` instances running simultaneously (`max_parallel` config)
39
+ - **Observability**: `/status` command shows task queue, chain progress, and execution stats
40
40
 
41
41
  ## Quick Start
42
42
 
@@ -149,7 +149,7 @@ All other interactions are handled naturally by Claude through the skill system
149
149
 
150
150
  When Claude determines a task is critical (deployment, deletion, production changes), it uses `ctl auto add-approval` instead of `ctl auto add`. The task enters `approval_pending` status:
151
151
 
152
- - **Telegram**: Inline keyboard with Approve / Reject buttons
152
+ - **Telegram**: Inline keyboard with Approve / Reject buttons
153
153
  - **Discord**: Bot sends approval request, user replies `!approve <id>` or `!reject <id>`
154
154
 
155
155
  Only after approval does the task enter the execution queue.
@@ -166,7 +166,7 @@ Task #1 (scout): "Analyze performance bottlenecks"
166
166
  Task #4: "Run benchmarks" (--parent 1)
167
167
  ```
168
168
 
169
- Chain progress is reported automatically: `📊 Chain #1 progress: 2/4 done`
169
+ Chain progress is reported automatically: `Chain #1 progress: 2/4 done`
170
170
 
171
171
  Results are persisted via `ctl auto result <id> "summary"` and cross-task context flows through the memory system.
172
172
 
@@ -299,11 +299,11 @@ MIT
299
299
 
300
300
  ### v0.5.0:Agent Gateway 特性
301
301
 
302
- - **🔒 人机协同 (HITL)**:关键任务需要用户通过内联按钮(Telegram)或命令(Discord)审批后才执行
303
- - **🔀 条件分支**:侦查模式 — 任务1分析,保存结果,动态创建后续任务并通过 `--parent` 关联
304
- - **🌐 Webhook 触发**:HTTP API + GitHub webhooks + 定时任务 — 从外部系统触发自动任务
305
- - **⚡ 并行执行**:多个 `claude` 实例同时运行(`max_parallel` 配置)
306
- - **📊 可观测性**:`/status` 命令显示任务队列、链路进度和执行统计
302
+ - **人机协同 (HITL)**:关键任务需要用户通过内联按钮(Telegram)或命令(Discord)审批后才执行
303
+ - **条件分支**:侦查模式 — 任务1分析,保存结果,动态创建后续任务并通过 `--parent` 关联
304
+ - **Webhook 触发**:HTTP API + GitHub webhooks + 定时任务 — 从外部系统触发自动任务
305
+ - **并行执行**:多个 `claude` 实例同时运行(`max_parallel` 配置)
306
+ - **可观测性**:`/status` 命令显示任务队列、链路进度和执行统计
307
307
 
308
308
  ## 快速开始
309
309
 
@@ -352,7 +352,7 @@ Discord 还支持:`!approve <id>`、`!reject <id>` 用于 HITL 审批。
352
352
 
353
353
  当 Claude 判定任务为关键操作(部署、删除、生产变更)时,使用 `ctl auto add-approval` 代替 `ctl auto add`。任务进入 `approval_pending` 状态:
354
354
 
355
- - **Telegram**:内联键盘显示 批准 / 拒绝 按钮
355
+ - **Telegram**:内联键盘显示 Approve / Reject 按钮
356
356
  - **Discord**:Bot 发送审批请求,用户回复 `!approve <id>` 或 `!reject <id>`
357
357
 
358
358
  只有审批通过后,任务才进入执行队列。
@@ -369,7 +369,7 @@ Discord 还支持:`!approve <id>`、`!reject <id>` 用于 HITL 审批。
369
369
  任务 #4:"运行基准测试" (--parent 1)
370
370
  ```
371
371
 
372
- 链路进度自动报告:`📊 任务链 #1 进度:2/4 完成`
372
+ 链路进度自动报告:`Chain #1 progress: 2/4 done`
373
373
 
374
374
  ## Webhook 与定时任务
375
375
 
@@ -159,7 +159,7 @@ export class DiscordAdapter {
159
159
  const now = Date.now();
160
160
  if (now - lastEdit < EDIT_INTERVAL)
161
161
  return;
162
- const preview = full.slice(-1900) + "\n\n⏳...";
162
+ const preview = full.slice(-1900) + "\n\n...";
163
163
  if (preview === lastText)
164
164
  return;
165
165
  lastText = preview;
@@ -292,13 +292,12 @@ export class DiscordAdapter {
292
292
  return;
293
293
  }
294
294
  const statusEmoji = {
295
- auto: "", running: "🔄", done: "", failed: "",
296
- approval_pending: "📋", cancelled: "🚫",
295
+ auto: "[queue]", running: "[run]", done: "[done]", failed: "[fail]",
296
+ approval_pending: "[pending]", cancelled: "[cancel]",
297
297
  };
298
298
  const lines = recent.map(task => {
299
- const emoji = statusEmoji[task.status] || "❓";
300
299
  const chain = task.parent_id ? ` (chain #${task.parent_id})` : "";
301
- return `${emoji} #${task.id} [${task.status}] ${task.description.slice(0, 60)}${chain}`;
300
+ return `${statusEmoji[task.status] || "[?]"} #${task.id} [${task.status}] ${task.description.slice(0, 60)}${chain}`;
302
301
  });
303
302
  const stats = this.store.getAutoTaskStats();
304
303
  const summary = stats.map(s => `${s.status}: ${s.count}`).join(" | ");
@@ -266,7 +266,7 @@ export class TelegramAdapter {
266
266
  if (now - lastEdit < EDIT_INTERVAL)
267
267
  return;
268
268
  lastEdit = now;
269
- const preview = full.slice(-3500) + "\n\n⏳...";
269
+ const preview = full.slice(-3500) + "\n\n...";
270
270
  await this.editMsg(chatId, msgId, preview);
271
271
  });
272
272
  console.log(`[telegram] claude done for ${uid}, cost=$${res.cost?.toFixed(4)}`);
@@ -423,8 +423,8 @@ export class TelegramAdapter {
423
423
  const chatId = Number(task.chat_id);
424
424
  const keyboard = {
425
425
  inline_keyboard: [[
426
- { text: "Approve", callback_data: `approve:${task.id}` },
427
- { text: "Reject", callback_data: `reject:${task.id}` },
426
+ { text: "Approve", callback_data: `approve:${task.id}` },
427
+ { text: "Reject", callback_data: `reject:${task.id}` },
428
428
  ]],
429
429
  };
430
430
  await this.call("sendMessage", {
@@ -496,13 +496,12 @@ export class TelegramAdapter {
496
496
  return;
497
497
  }
498
498
  const statusEmoji = {
499
- auto: "", running: "🔄", done: "", failed: "",
500
- approval_pending: "📋", cancelled: "🚫",
499
+ auto: "[queue]", running: "[run]", done: "[done]", failed: "[fail]",
500
+ approval_pending: "[pending]", cancelled: "[cancel]",
501
501
  };
502
502
  const lines = recent.map(task => {
503
- const emoji = statusEmoji[task.status] || "❓";
504
503
  const chain = task.parent_id ? ` (chain #${task.parent_id})` : "";
505
- return `${emoji} #${task.id} [${task.status}] ${task.description.slice(0, 60)}${chain}`;
504
+ return `${statusEmoji[task.status] || "[?]"} #${task.id} [${task.status}] ${task.description.slice(0, 60)}${chain}`;
506
505
  });
507
506
  const stats = this.store.getAutoTaskStats();
508
507
  const summary = stats.map(s => `${s.status}: ${s.count}`).join(" | ");
@@ -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,7 +146,7 @@ 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(" ")}`);
@@ -252,7 +252,7 @@ export class AgentEngine {
252
252
  env.ANTHROPIC_API_KEY = ep.api_key;
253
253
  if (ep.base_url)
254
254
  env.ANTHROPIC_BASE_URL = ep.base_url;
255
- env.CLAUDEBRIDGE_DB = pathResolve("./data/claudebridge.db");
255
+ env.CLAUDEBRIDGE_DB = this.store.dbPath;
256
256
  const child = spawn("claude", args, { cwd, env, stdio: ["pipe", "pipe", "pipe"] });
257
257
  child.stdin.end();
258
258
  console.log(`[agent] spawned claude (parallel) pid=${child.pid} cwd=${cwd}`);
package/dist/core/i18n.js CHANGED
@@ -6,21 +6,21 @@ const messages = {
6
6
  no_history: "No history.",
7
7
  config_reloaded: "Config reloaded.",
8
8
  reload_failed: "Reload failed: ",
9
- thinking: "Thinking...",
10
- still_processing: "Still processing...",
9
+ thinking: "Thinking...",
10
+ still_processing: "Still processing...",
11
11
  upload_failed: "Upload failed: ",
12
- reminder_notify: "Reminder: {desc}",
13
- auto_starting: "🤖 Auto task #{id} starting:\n{desc}",
14
- auto_done: "Auto task #{id} done (cost: ${cost}):",
15
- auto_failed: "Auto task #{id} failed: {err}",
12
+ reminder_notify: "Reminder: {desc}",
13
+ auto_starting: "Auto task #{id} starting:\n{desc}",
14
+ auto_done: "Auto task #{id} done (cost: ${cost}):",
15
+ auto_failed: "Auto task #{id} failed: {err}",
16
16
  page_expired: "Page expired. Please resend your question.",
17
- approval_request: "🔒 Approval needed for auto task #{id}:\n{desc}",
18
- approval_approved: "Auto task #{id} approved queued for execution.",
19
- approval_rejected: "Auto task #{id} rejected.",
17
+ approval_request: "Approval needed for auto task #{id}:\n{desc}",
18
+ approval_approved: "Auto task #{id} approved -- queued for execution.",
19
+ approval_rejected: "Auto task #{id} rejected.",
20
20
  approval_decided: "Task #{id} has already been decided.",
21
21
  no_auto_tasks: "No auto tasks found.",
22
- status_report: "📊 Auto Task Status:",
23
- chain_progress: "📊 Chain #{id} progress: {done}/{total} done{cost}",
22
+ status_report: "Auto Task Status:",
23
+ chain_progress: "Chain #{id} progress: {done}/{total} done{cost}",
24
24
  },
25
25
  zh: {
26
26
  help: "ClaudeBridge 就绪。\n\n管理命令:\n/new - 清除会话\n/usage - 你的用量\n/allusage - 所有用量\n/history - 最近对话\n/model - 端点信息\n/status - 自动任务状态\n/reload - 重载配置\n/help - 显示帮助\n\n直接对话即可管理记忆、任务、提醒等 — Claude 会自动处理。",
@@ -29,21 +29,21 @@ const messages = {
29
29
  no_history: "暂无历史记录。",
30
30
  config_reloaded: "配置已重载。",
31
31
  reload_failed: "重载失败:",
32
- thinking: "思考中...",
33
- still_processing: "仍在处理...",
32
+ thinking: "思考中...",
33
+ still_processing: "仍在处理...",
34
34
  upload_failed: "上传失败:",
35
- reminder_notify: "提醒:{desc}",
36
- auto_starting: "🤖 自动任务 #{id} 开始执行:\n{desc}",
37
- auto_done: "自动任务 #{id} 完成(花费:${cost}):",
38
- auto_failed: "自动任务 #{id} 失败:{err}",
35
+ reminder_notify: "提醒:{desc}",
36
+ auto_starting: "自动任务 #{id} 开始执行:\n{desc}",
37
+ auto_done: "自动任务 #{id} 完成(花费:${cost}):",
38
+ auto_failed: "自动任务 #{id} 失败:{err}",
39
39
  page_expired: "页面已过期,请重新发送问题。",
40
- approval_request: "🔒 自动任务 #{id} 需要审批:\n{desc}",
41
- approval_approved: "自动任务 #{id} 已批准 已加入执行队列。",
42
- approval_rejected: "自动任务 #{id} 已拒绝。",
40
+ approval_request: "自动任务 #{id} 需要审批:\n{desc}",
41
+ approval_approved: "自动任务 #{id} 已批准 -- 已加入执行队列。",
42
+ approval_rejected: "自动任务 #{id} 已拒绝。",
43
43
  approval_decided: "任务 #{id} 已被处理。",
44
44
  no_auto_tasks: "暂无自动任务。",
45
- status_report: "📊 自动任务状态:",
46
- chain_progress: "📊 任务链 #{id} 进度:{done}/{total} 完成{cost}",
45
+ status_report: "自动任务状态:",
46
+ chain_progress: "任务链 #{id} 进度:{done}/{total} 完成{cost}",
47
47
  },
48
48
  };
49
49
  const commandDescriptions = {
@@ -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.0",
3
+ "version": "0.5.2",
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": {