@emqo/claudebridge 0.1.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/README.md +228 -0
- package/config.yaml.example +52 -0
- package/dist/adapters/base.d.ts +12 -0
- package/dist/adapters/base.js +19 -0
- package/dist/adapters/discord.d.ts +18 -0
- package/dist/adapters/discord.js +313 -0
- package/dist/adapters/telegram.d.ts +27 -0
- package/dist/adapters/telegram.js +418 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +111 -0
- package/dist/core/agent.d.ts +32 -0
- package/dist/core/agent.js +218 -0
- package/dist/core/config.d.ts +58 -0
- package/dist/core/config.js +49 -0
- package/dist/core/i18n.d.ts +5 -0
- package/dist/core/i18n.js +102 -0
- package/dist/core/intent.d.ts +10 -0
- package/dist/core/intent.js +84 -0
- package/dist/core/keys.d.ts +22 -0
- package/dist/core/keys.js +42 -0
- package/dist/core/lock.d.ts +12 -0
- package/dist/core/lock.js +57 -0
- package/dist/core/markdown.d.ts +7 -0
- package/dist/core/markdown.js +51 -0
- package/dist/core/permissions.d.ts +8 -0
- package/dist/core/permissions.js +22 -0
- package/dist/core/store.d.ts +64 -0
- package/dist/core/store.js +145 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +75 -0
- package/package.json +46 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Escape text for Telegram MarkdownV2 */
|
|
2
|
+
export function escapeMarkdownV2(text) {
|
|
3
|
+
// Characters that must be escaped outside code blocks
|
|
4
|
+
return text.replace(/([_*\[\]()~`>#+\-=|{}.!\\])/g, "\\$1");
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Convert standard markdown to Telegram MarkdownV2.
|
|
8
|
+
* Handles code blocks (preserve as-is), inline code, bold, italic, links.
|
|
9
|
+
*/
|
|
10
|
+
export function toTelegramMarkdown(text) {
|
|
11
|
+
const parts = [];
|
|
12
|
+
// Split by code blocks first (``` ... ```)
|
|
13
|
+
const segments = text.split(/(```[\s\S]*?```)/g);
|
|
14
|
+
for (const seg of segments) {
|
|
15
|
+
if (seg.startsWith("```")) {
|
|
16
|
+
// Code block: extract lang and content, only escape backslash and backtick inside
|
|
17
|
+
const match = seg.match(/^```(\w*)\n?([\s\S]*?)```$/);
|
|
18
|
+
if (match) {
|
|
19
|
+
const lang = match[1];
|
|
20
|
+
const code = match[2].replace(/([\\`])/g, "\\$1");
|
|
21
|
+
parts.push(`\`\`\`${lang}\n${code}\`\`\``);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
parts.push(seg);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Process inline elements
|
|
29
|
+
let s = seg;
|
|
30
|
+
// Protect inline code first
|
|
31
|
+
const inlineCodes = [];
|
|
32
|
+
s = s.replace(/`([^`]+)`/g, (_, code) => {
|
|
33
|
+
const escaped = code.replace(/([\\`])/g, "\\$1");
|
|
34
|
+
inlineCodes.push(`\`${escaped}\``);
|
|
35
|
+
return `\x00IC${inlineCodes.length - 1}\x00`;
|
|
36
|
+
});
|
|
37
|
+
// Escape special chars in normal text
|
|
38
|
+
s = escapeMarkdownV2(s);
|
|
39
|
+
// Restore bold **text** → *text*
|
|
40
|
+
s = s.replace(/\\\*\\\*(.+?)\\\*\\\*/g, "*$1*");
|
|
41
|
+
// Restore italic _text_ (single underscore)
|
|
42
|
+
s = s.replace(/\\_(.+?)\\_/g, "_$1_");
|
|
43
|
+
// Restore links [text](url)
|
|
44
|
+
s = s.replace(/\\\[(.+?)\\\]\\\((.+?)\\\)/g, "[$1]($2)");
|
|
45
|
+
// Restore inline codes
|
|
46
|
+
s = s.replace(/\x00IC(\d+)\x00/g, (_, i) => inlineCodes[Number(i)]);
|
|
47
|
+
parts.push(s);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return parts.join("");
|
|
51
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Simple whitelist: allowed users + groups. Empty = allow all. */
|
|
2
|
+
export declare class AccessControl {
|
|
3
|
+
private users;
|
|
4
|
+
private groups;
|
|
5
|
+
constructor(users?: string[], groups?: string[]);
|
|
6
|
+
isAllowed(userId: string, groupId?: string): boolean;
|
|
7
|
+
reload(users: string[], groups: string[]): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Simple whitelist: allowed users + groups. Empty = allow all. */
|
|
2
|
+
export class AccessControl {
|
|
3
|
+
users;
|
|
4
|
+
groups;
|
|
5
|
+
constructor(users = [], groups = []) {
|
|
6
|
+
this.users = new Set(users.map(String));
|
|
7
|
+
this.groups = new Set(groups.map(String));
|
|
8
|
+
}
|
|
9
|
+
isAllowed(userId, groupId) {
|
|
10
|
+
if (!this.users.size && !this.groups.size)
|
|
11
|
+
return true;
|
|
12
|
+
if (this.users.has(String(userId)))
|
|
13
|
+
return true;
|
|
14
|
+
if (groupId && this.groups.has(String(groupId)))
|
|
15
|
+
return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
reload(users, groups) {
|
|
19
|
+
this.users = new Set(users.map(String));
|
|
20
|
+
this.groups = new Set(groups.map(String));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export declare class Store {
|
|
2
|
+
private db;
|
|
3
|
+
constructor();
|
|
4
|
+
getSession(userId: string): string | null;
|
|
5
|
+
setSession(userId: string, sessionId: string, platform: string): void;
|
|
6
|
+
clearSession(userId: string): void;
|
|
7
|
+
recordUsage(userId: string, platform: string, costUsd: number): void;
|
|
8
|
+
getUsage(userId: string): {
|
|
9
|
+
total_cost: number;
|
|
10
|
+
count: number;
|
|
11
|
+
};
|
|
12
|
+
getUsageAll(): {
|
|
13
|
+
user_id: string;
|
|
14
|
+
total_cost: number;
|
|
15
|
+
count: number;
|
|
16
|
+
}[];
|
|
17
|
+
addHistory(userId: string, platform: string, role: string, content: string): void;
|
|
18
|
+
getHistory(userId: string, limit?: number): {
|
|
19
|
+
role: string;
|
|
20
|
+
content: string;
|
|
21
|
+
created_at: number;
|
|
22
|
+
}[];
|
|
23
|
+
addMemory(userId: string, content: string, source?: string): void;
|
|
24
|
+
getMemories(userId: string): {
|
|
25
|
+
id: number;
|
|
26
|
+
content: string;
|
|
27
|
+
source: string;
|
|
28
|
+
created_at: number;
|
|
29
|
+
}[];
|
|
30
|
+
clearMemories(userId: string): void;
|
|
31
|
+
trimMemories(userId: string, max: number): void;
|
|
32
|
+
addTask(userId: string, platform: string, chatId: string, description: string, remindAt?: number, auto?: boolean): number;
|
|
33
|
+
getTasks(userId: string): {
|
|
34
|
+
id: number;
|
|
35
|
+
description: string;
|
|
36
|
+
status: string;
|
|
37
|
+
remind_at: number | null;
|
|
38
|
+
created_at: number;
|
|
39
|
+
}[];
|
|
40
|
+
completeTask(taskId: number, userId: string): boolean;
|
|
41
|
+
getDueReminders(): {
|
|
42
|
+
id: number;
|
|
43
|
+
user_id: string;
|
|
44
|
+
platform: string;
|
|
45
|
+
chat_id: string;
|
|
46
|
+
description: string;
|
|
47
|
+
}[];
|
|
48
|
+
markReminderSent(taskId: number): void;
|
|
49
|
+
getNextAutoTask(): {
|
|
50
|
+
id: number;
|
|
51
|
+
user_id: string;
|
|
52
|
+
platform: string;
|
|
53
|
+
chat_id: string;
|
|
54
|
+
description: string;
|
|
55
|
+
} | null;
|
|
56
|
+
markTaskRunning(taskId: number): void;
|
|
57
|
+
markTaskResult(taskId: number, status: string): void;
|
|
58
|
+
getAutoTasks(userId: string): {
|
|
59
|
+
id: number;
|
|
60
|
+
description: string;
|
|
61
|
+
status: string;
|
|
62
|
+
created_at: number;
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { mkdirSync } from "fs";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
const DB_PATH = "./data/claudebridge.db";
|
|
5
|
+
export class Store {
|
|
6
|
+
db;
|
|
7
|
+
constructor() {
|
|
8
|
+
mkdirSync(dirname(DB_PATH), { recursive: true });
|
|
9
|
+
this.db = new Database(DB_PATH);
|
|
10
|
+
this.db.pragma("journal_mode = WAL");
|
|
11
|
+
this.db.exec(`
|
|
12
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
13
|
+
user_id TEXT PRIMARY KEY,
|
|
14
|
+
session_id TEXT NOT NULL,
|
|
15
|
+
platform TEXT NOT NULL,
|
|
16
|
+
updated_at INTEGER NOT NULL
|
|
17
|
+
);
|
|
18
|
+
CREATE TABLE IF NOT EXISTS usage (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
user_id TEXT NOT NULL,
|
|
21
|
+
platform TEXT NOT NULL,
|
|
22
|
+
cost_usd REAL NOT NULL DEFAULT 0,
|
|
23
|
+
created_at INTEGER NOT NULL
|
|
24
|
+
);
|
|
25
|
+
CREATE TABLE IF NOT EXISTS history (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
user_id TEXT NOT NULL,
|
|
28
|
+
platform TEXT NOT NULL,
|
|
29
|
+
role TEXT NOT NULL,
|
|
30
|
+
content TEXT NOT NULL,
|
|
31
|
+
created_at INTEGER NOT NULL
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_usage_user ON usage(user_id);
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_history_user ON history(user_id, created_at);
|
|
35
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
36
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37
|
+
user_id TEXT NOT NULL,
|
|
38
|
+
content TEXT NOT NULL,
|
|
39
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
40
|
+
created_at INTEGER NOT NULL
|
|
41
|
+
);
|
|
42
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
43
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
44
|
+
user_id TEXT NOT NULL,
|
|
45
|
+
platform TEXT NOT NULL,
|
|
46
|
+
chat_id TEXT NOT NULL,
|
|
47
|
+
description TEXT NOT NULL,
|
|
48
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
49
|
+
remind_at INTEGER,
|
|
50
|
+
reminder_sent INTEGER NOT NULL DEFAULT 0,
|
|
51
|
+
created_at INTEGER NOT NULL
|
|
52
|
+
);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_memories_user ON memories(user_id);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_user ON tasks(user_id, status);
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
// --- sessions ---
|
|
58
|
+
getSession(userId) {
|
|
59
|
+
const row = this.db
|
|
60
|
+
.prepare("SELECT session_id FROM sessions WHERE user_id = ?")
|
|
61
|
+
.get(userId);
|
|
62
|
+
return row?.session_id ?? null;
|
|
63
|
+
}
|
|
64
|
+
setSession(userId, sessionId, platform) {
|
|
65
|
+
this.db
|
|
66
|
+
.prepare(`INSERT INTO sessions (user_id, session_id, platform, updated_at)
|
|
67
|
+
VALUES (?, ?, ?, ?)
|
|
68
|
+
ON CONFLICT(user_id) DO UPDATE SET session_id=?, updated_at=?`)
|
|
69
|
+
.run(userId, sessionId, platform, Date.now(), sessionId, Date.now());
|
|
70
|
+
}
|
|
71
|
+
clearSession(userId) {
|
|
72
|
+
this.db.prepare("DELETE FROM sessions WHERE user_id = ?").run(userId);
|
|
73
|
+
}
|
|
74
|
+
// --- usage ---
|
|
75
|
+
recordUsage(userId, platform, costUsd) {
|
|
76
|
+
this.db
|
|
77
|
+
.prepare("INSERT INTO usage (user_id, platform, cost_usd, created_at) VALUES (?, ?, ?, ?)")
|
|
78
|
+
.run(userId, platform, costUsd, Date.now());
|
|
79
|
+
}
|
|
80
|
+
getUsage(userId) {
|
|
81
|
+
const row = this.db
|
|
82
|
+
.prepare("SELECT COALESCE(SUM(cost_usd),0) as total_cost, COUNT(*) as count FROM usage WHERE user_id = ?")
|
|
83
|
+
.get(userId);
|
|
84
|
+
return row;
|
|
85
|
+
}
|
|
86
|
+
getUsageAll() {
|
|
87
|
+
return this.db
|
|
88
|
+
.prepare("SELECT user_id, COALESCE(SUM(cost_usd),0) as total_cost, COUNT(*) as count FROM usage GROUP BY user_id ORDER BY total_cost DESC")
|
|
89
|
+
.all();
|
|
90
|
+
}
|
|
91
|
+
// --- history ---
|
|
92
|
+
addHistory(userId, platform, role, content) {
|
|
93
|
+
this.db
|
|
94
|
+
.prepare("INSERT INTO history (user_id, platform, role, content, created_at) VALUES (?, ?, ?, ?, ?)")
|
|
95
|
+
.run(userId, platform, role, content, Date.now());
|
|
96
|
+
}
|
|
97
|
+
getHistory(userId, limit = 10) {
|
|
98
|
+
return this.db
|
|
99
|
+
.prepare("SELECT role, content, created_at FROM history WHERE user_id = ? ORDER BY created_at DESC LIMIT ?")
|
|
100
|
+
.all(userId, limit);
|
|
101
|
+
}
|
|
102
|
+
// --- memories ---
|
|
103
|
+
addMemory(userId, content, source = "manual") {
|
|
104
|
+
this.db.prepare("INSERT INTO memories (user_id, content, source, created_at) VALUES (?, ?, ?, ?)").run(userId, content, source, Date.now());
|
|
105
|
+
}
|
|
106
|
+
getMemories(userId) {
|
|
107
|
+
return this.db.prepare("SELECT id, content, source, created_at FROM memories WHERE user_id = ? ORDER BY created_at DESC").all(userId);
|
|
108
|
+
}
|
|
109
|
+
clearMemories(userId) {
|
|
110
|
+
this.db.prepare("DELETE FROM memories WHERE user_id = ?").run(userId);
|
|
111
|
+
}
|
|
112
|
+
trimMemories(userId, max) {
|
|
113
|
+
this.db.prepare("DELETE FROM memories WHERE user_id = ? AND id NOT IN (SELECT id FROM memories WHERE user_id = ? ORDER BY created_at DESC LIMIT ?)").run(userId, userId, max);
|
|
114
|
+
}
|
|
115
|
+
// --- tasks ---
|
|
116
|
+
addTask(userId, platform, chatId, description, remindAt, auto = false) {
|
|
117
|
+
const r = this.db.prepare("INSERT INTO tasks (user_id, platform, chat_id, description, status, remind_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)").run(userId, platform, chatId, description, auto ? "auto" : "pending", remindAt ?? null, Date.now());
|
|
118
|
+
return Number(r.lastInsertRowid);
|
|
119
|
+
}
|
|
120
|
+
getTasks(userId) {
|
|
121
|
+
return this.db.prepare("SELECT id, description, status, remind_at, created_at FROM tasks WHERE user_id = ? AND status = 'pending' ORDER BY created_at DESC").all(userId);
|
|
122
|
+
}
|
|
123
|
+
completeTask(taskId, userId) {
|
|
124
|
+
const r = this.db.prepare("UPDATE tasks SET status = 'done' WHERE id = ? AND user_id = ? AND status = 'pending'").run(taskId, userId);
|
|
125
|
+
return r.changes > 0;
|
|
126
|
+
}
|
|
127
|
+
getDueReminders() {
|
|
128
|
+
return this.db.prepare("SELECT id, user_id, platform, chat_id, description FROM tasks WHERE status = 'pending' AND remind_at IS NOT NULL AND remind_at <= ? AND reminder_sent = 0").all(Date.now());
|
|
129
|
+
}
|
|
130
|
+
markReminderSent(taskId) {
|
|
131
|
+
this.db.prepare("UPDATE tasks SET reminder_sent = 1 WHERE id = ?").run(taskId);
|
|
132
|
+
}
|
|
133
|
+
getNextAutoTask() {
|
|
134
|
+
return this.db.prepare("SELECT id, user_id, platform, chat_id, description FROM tasks WHERE status = 'auto' ORDER BY created_at ASC LIMIT 1").get() ?? null;
|
|
135
|
+
}
|
|
136
|
+
markTaskRunning(taskId) {
|
|
137
|
+
this.db.prepare("UPDATE tasks SET status = 'running' WHERE id = ?").run(taskId);
|
|
138
|
+
}
|
|
139
|
+
markTaskResult(taskId, status) {
|
|
140
|
+
this.db.prepare("UPDATE tasks SET status = ? WHERE id = ?").run(status, taskId);
|
|
141
|
+
}
|
|
142
|
+
getAutoTasks(userId) {
|
|
143
|
+
return this.db.prepare("SELECT id, description, status, created_at FROM tasks WHERE user_id = ? AND status IN ('auto','running') ORDER BY created_at DESC").all(userId);
|
|
144
|
+
}
|
|
145
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { setDefaultAutoSelectFamily } from "net";
|
|
2
|
+
setDefaultAutoSelectFamily(false);
|
|
3
|
+
import { watch } from "fs";
|
|
4
|
+
import { loadConfig, reloadConfig } from "./core/config.js";
|
|
5
|
+
import { Store } from "./core/store.js";
|
|
6
|
+
import { AgentEngine } from "./core/agent.js";
|
|
7
|
+
import { TelegramAdapter } from "./adapters/telegram.js";
|
|
8
|
+
import { DiscordAdapter } from "./adapters/discord.js";
|
|
9
|
+
async function main() {
|
|
10
|
+
const _cfgIdx = process.argv.indexOf("--config");
|
|
11
|
+
const _cfgPath = _cfgIdx !== -1 ? process.argv[_cfgIdx + 1] : undefined;
|
|
12
|
+
let config = loadConfig(_cfgPath);
|
|
13
|
+
const store = new Store();
|
|
14
|
+
const engine = new AgentEngine(config, store);
|
|
15
|
+
const adapters = [];
|
|
16
|
+
if (config.platforms.telegram.enabled) {
|
|
17
|
+
if (!config.platforms.telegram.token) {
|
|
18
|
+
console.error("[fatal] TELEGRAM_BOT_TOKEN not set");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
adapters.push(new TelegramAdapter(engine, store, config.platforms.telegram, config.locale));
|
|
22
|
+
}
|
|
23
|
+
if (config.platforms.discord.enabled) {
|
|
24
|
+
if (!config.platforms.discord.token) {
|
|
25
|
+
console.error("[fatal] DISCORD_BOT_TOKEN not set");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
adapters.push(new DiscordAdapter(engine, store, config.platforms.discord, config.locale));
|
|
29
|
+
}
|
|
30
|
+
if (!adapters.length) {
|
|
31
|
+
console.error("[fatal] no platform enabled");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
for (const a of adapters)
|
|
35
|
+
await a.start();
|
|
36
|
+
console.log(`[claudebridge] running with ${adapters.length} adapter(s)`);
|
|
37
|
+
// Hot reload config.yaml
|
|
38
|
+
let reloadTimer = null;
|
|
39
|
+
watch(_cfgPath || "config.yaml", () => {
|
|
40
|
+
if (reloadTimer)
|
|
41
|
+
clearTimeout(reloadTimer);
|
|
42
|
+
reloadTimer = setTimeout(() => {
|
|
43
|
+
try {
|
|
44
|
+
config = reloadConfig();
|
|
45
|
+
engine.reloadConfig(config);
|
|
46
|
+
console.log("[claudebridge] config reloaded");
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error("[claudebridge] config reload failed:", err);
|
|
50
|
+
}
|
|
51
|
+
}, 500); // debounce
|
|
52
|
+
});
|
|
53
|
+
const shutdown = () => {
|
|
54
|
+
console.log("[claudebridge] shutting down...");
|
|
55
|
+
for (const a of adapters)
|
|
56
|
+
a.stop();
|
|
57
|
+
process.exit(0);
|
|
58
|
+
};
|
|
59
|
+
process.on("SIGINT", shutdown);
|
|
60
|
+
process.on("SIGTERM", shutdown);
|
|
61
|
+
process.on("SIGHUP", () => {
|
|
62
|
+
try {
|
|
63
|
+
config = reloadConfig();
|
|
64
|
+
engine.reloadConfig(config);
|
|
65
|
+
console.log("[claudebridge] config reloaded (SIGHUP)");
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error("[claudebridge] config reload failed:", err);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
main().catch((err) => {
|
|
73
|
+
console.error("[fatal]", err);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@emqo/claudebridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bridge Claude Code Agent SDK to chat platforms (Telegram, Discord, etc.)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claudebridge": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsx src/index.ts"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"claude",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"telegram",
|
|
19
|
+
"bridge",
|
|
20
|
+
"agent"
|
|
21
|
+
],
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"config.yaml.example"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.50",
|
|
29
|
+
"better-sqlite3": "^12.6.2",
|
|
30
|
+
"discord.js": "^14.25.1",
|
|
31
|
+
"dotenv": "^17.3.1",
|
|
32
|
+
"grammy": "^1.40.0",
|
|
33
|
+
"ioredis": "^5.9.3",
|
|
34
|
+
"yaml": "^2.8.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
38
|
+
"@types/ioredis": "^4.28.10",
|
|
39
|
+
"@types/node": "^25.3.0",
|
|
40
|
+
"tsx": "^4.21.0",
|
|
41
|
+
"typescript": "^5.9.3"
|
|
42
|
+
},
|
|
43
|
+
"overrides": {
|
|
44
|
+
"undici": ">=6.23.0"
|
|
45
|
+
}
|
|
46
|
+
}
|