@wu529778790/open-im 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.
Files changed (58) hide show
  1. package/.env.example +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +51 -0
  4. package/dist/access/access-control.d.ts +5 -0
  5. package/dist/access/access-control.js +11 -0
  6. package/dist/adapters/claude-adapter.d.ts +7 -0
  7. package/dist/adapters/claude-adapter.js +17 -0
  8. package/dist/adapters/registry.d.ts +4 -0
  9. package/dist/adapters/registry.js +11 -0
  10. package/dist/adapters/tool-adapter.interface.d.ts +35 -0
  11. package/dist/adapters/tool-adapter.interface.js +4 -0
  12. package/dist/claude/cli-runner.d.ts +28 -0
  13. package/dist/claude/cli-runner.js +166 -0
  14. package/dist/claude/stream-parser.d.ts +17 -0
  15. package/dist/claude/stream-parser.js +50 -0
  16. package/dist/claude/types.d.ts +54 -0
  17. package/dist/claude/types.js +21 -0
  18. package/dist/cli.d.ts +2 -0
  19. package/dist/cli.js +6 -0
  20. package/dist/commands/handler.d.ts +30 -0
  21. package/dist/commands/handler.js +122 -0
  22. package/dist/config.d.ts +20 -0
  23. package/dist/config.js +88 -0
  24. package/dist/constants.d.ts +7 -0
  25. package/dist/constants.js +15 -0
  26. package/dist/hook/permission-server.d.ts +4 -0
  27. package/dist/hook/permission-server.js +12 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +76 -0
  30. package/dist/logger.d.ts +16 -0
  31. package/dist/logger.js +65 -0
  32. package/dist/queue/request-queue.d.ts +6 -0
  33. package/dist/queue/request-queue.js +43 -0
  34. package/dist/sanitize.d.ts +1 -0
  35. package/dist/sanitize.js +11 -0
  36. package/dist/session/session-manager.d.ts +26 -0
  37. package/dist/session/session-manager.js +207 -0
  38. package/dist/setup.d.ts +5 -0
  39. package/dist/setup.js +97 -0
  40. package/dist/shared/active-chats.d.ts +4 -0
  41. package/dist/shared/active-chats.js +55 -0
  42. package/dist/shared/ai-task.d.ts +42 -0
  43. package/dist/shared/ai-task.js +167 -0
  44. package/dist/shared/message-dedup.d.ts +4 -0
  45. package/dist/shared/message-dedup.js +25 -0
  46. package/dist/shared/task-cleanup.d.ts +2 -0
  47. package/dist/shared/task-cleanup.js +19 -0
  48. package/dist/shared/types.d.ts +9 -0
  49. package/dist/shared/types.js +1 -0
  50. package/dist/shared/utils.d.ts +7 -0
  51. package/dist/shared/utils.js +72 -0
  52. package/dist/telegram/client.d.ts +6 -0
  53. package/dist/telegram/client.js +27 -0
  54. package/dist/telegram/event-handler.d.ts +8 -0
  55. package/dist/telegram/event-handler.js +174 -0
  56. package/dist/telegram/message-sender.d.ts +7 -0
  57. package/dist/telegram/message-sender.js +102 -0
  58. package/package.json +52 -0
@@ -0,0 +1,72 @@
1
+ const TOOL_EMOJIS = {
2
+ Read: 'šŸ“–', Write: 'āœļø', Edit: 'šŸ“', Bash: 'šŸ’»', Glob: 'šŸ”', Grep: 'šŸ”Ž',
3
+ WebFetch: '🌐', WebSearch: 'šŸ”Ž', Task: 'šŸ“‹', TodoRead: 'šŸ“Œ', TodoWrite: 'āœ…',
4
+ };
5
+ function getToolEmoji(name) {
6
+ return TOOL_EMOJIS[name] ?? 'šŸ”§';
7
+ }
8
+ export function truncateText(text, maxLen) {
9
+ if (text.length <= maxLen)
10
+ return text;
11
+ const keepLen = maxLen - 20;
12
+ const tail = text.slice(text.length - keepLen);
13
+ const lineBreak = tail.indexOf('\n');
14
+ const clean = lineBreak > 0 && lineBreak < 200 ? tail.slice(lineBreak + 1) : tail;
15
+ return `...(å‰ę–‡å·²ēœē•„)...\n${clean}`;
16
+ }
17
+ export function splitLongContent(text, maxLen) {
18
+ if (text.length <= maxLen)
19
+ return [text];
20
+ const parts = [];
21
+ let start = 0;
22
+ while (start < text.length) {
23
+ let end = start + maxLen;
24
+ if (end >= text.length) {
25
+ parts.push(text.slice(start));
26
+ break;
27
+ }
28
+ const lastNewline = text.lastIndexOf('\n', end);
29
+ if (lastNewline > start)
30
+ end = lastNewline + 1;
31
+ parts.push(text.slice(start, end));
32
+ start = end;
33
+ }
34
+ return parts;
35
+ }
36
+ export function formatToolStats(toolStats, numTurns) {
37
+ const total = Object.values(toolStats).reduce((a, b) => a + b, 0);
38
+ if (total === 0)
39
+ return '';
40
+ const parts = Object.entries(toolStats)
41
+ .sort((a, b) => b[1] - a[1])
42
+ .map(([name, count]) => `${getToolEmoji(name)}${name}Ɨ${count}`)
43
+ .join(' ');
44
+ return `${numTurns > 0 ? numTurns + ' č½® ' : ''}${total} ę¬”å·„å…·ļ¼ˆ${parts})`;
45
+ }
46
+ export function formatToolCallNotification(toolName, toolInput) {
47
+ const emoji = getToolEmoji(toolName);
48
+ if (!toolInput)
49
+ return `${emoji} ${toolName}`;
50
+ let detail = '';
51
+ if (toolName === 'Bash' && toolInput.command)
52
+ detail = ` → ${String(toolInput.command).slice(0, 60)}`;
53
+ if (toolName === 'Read' && toolInput.file_path)
54
+ detail = ` → ${toolInput.file_path}`;
55
+ if (toolName === 'Write' && toolInput.file_path)
56
+ detail = ` → ${toolInput.file_path}`;
57
+ return `${emoji} ${toolName}${detail}`;
58
+ }
59
+ export function trackCost(userCosts, userId, cost, durationMs) {
60
+ const r = userCosts.get(userId) ?? { totalCost: 0, totalDurationMs: 0, requestCount: 0 };
61
+ r.totalCost += cost;
62
+ r.totalDurationMs += durationMs;
63
+ r.requestCount += 1;
64
+ userCosts.set(userId, r);
65
+ }
66
+ export function getContextWarning(totalTurns) {
67
+ if (totalTurns >= 12)
68
+ return 'āš ļø äøŠäø‹ę–‡č¾ƒé•æļ¼Œå»ŗč®® /new å¼€å§‹ę–°ä¼ščÆ';
69
+ if (totalTurns >= 8)
70
+ return `šŸ’” åÆ¹čÆå·² ${totalTurns} č½®ļ¼ŒåÆē”Ø /compact åŽ‹ē¼©`;
71
+ return null;
72
+ }
@@ -0,0 +1,6 @@
1
+ import { Telegraf } from 'telegraf';
2
+ import type { Config } from '../config.js';
3
+ export declare function getBot(): Telegraf;
4
+ export declare function getBotUsername(): string | undefined;
5
+ export declare function initTelegram(config: Config, setupHandlers: (bot: Telegraf) => void): Promise<void>;
6
+ export declare function stopTelegram(): void;
@@ -0,0 +1,27 @@
1
+ import { Telegraf } from 'telegraf';
2
+ import { createLogger } from '../logger.js';
3
+ const log = createLogger('Telegram');
4
+ let bot;
5
+ let botUsername;
6
+ export function getBot() {
7
+ if (!bot)
8
+ throw new Error('Telegram bot not initialized');
9
+ return bot;
10
+ }
11
+ export function getBotUsername() {
12
+ return botUsername;
13
+ }
14
+ export async function initTelegram(config, setupHandlers) {
15
+ bot = new Telegraf(config.telegramBotToken);
16
+ setupHandlers(bot);
17
+ const me = (await bot.telegram.getMe());
18
+ botUsername = me.username;
19
+ bot.launch().catch((err) => {
20
+ log.error('Telegram polling error:', err);
21
+ process.exit(1);
22
+ });
23
+ log.info('Telegram bot launched');
24
+ }
25
+ export function stopTelegram() {
26
+ bot?.stop('SIGTERM');
27
+ }
@@ -0,0 +1,8 @@
1
+ import type { Telegraf } from 'telegraf';
2
+ import type { Config } from '../config.js';
3
+ import type { SessionManager } from '../session/session-manager.js';
4
+ export interface TelegramEventHandlerHandle {
5
+ stop: () => void;
6
+ getRunningTaskCount: () => number;
7
+ }
8
+ export declare function setupTelegramHandlers(bot: Telegraf, config: Config, sessionManager: SessionManager): TelegramEventHandlerHandle;
@@ -0,0 +1,174 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { message } from 'telegraf/filters';
4
+ import { AccessControl } from '../access/access-control.js';
5
+ import { RequestQueue } from '../queue/request-queue.js';
6
+ import { sendThinkingMessage, updateMessage, sendFinalMessages, sendTextReply, startTypingLoop, sendImageReply, } from './message-sender.js';
7
+ import { registerPermissionSender, resolvePermissionById } from '../hook/permission-server.js';
8
+ import { CommandHandler } from '../commands/handler.js';
9
+ import { getAdapter } from '../adapters/registry.js';
10
+ import { runAITask } from '../shared/ai-task.js';
11
+ import { startTaskCleanup } from '../shared/task-cleanup.js';
12
+ import { MessageDedup } from '../shared/message-dedup.js';
13
+ import { THROTTLE_MS, IMAGE_DIR } from '../constants.js';
14
+ import { setActiveChatId } from '../shared/active-chats.js';
15
+ import { createLogger } from '../logger.js';
16
+ const log = createLogger('TgHandler');
17
+ async function downloadTelegramPhoto(bot, fileId) {
18
+ await mkdir(IMAGE_DIR, { recursive: true });
19
+ const fileLink = await bot.telegram.getFileLink(fileId);
20
+ const res = await fetch(fileLink.href, { signal: AbortSignal.timeout(30000) });
21
+ const buffer = Buffer.from(await res.arrayBuffer());
22
+ const safeId = fileId.replace(/[^a-zA-Z0-9_-]/g, '_');
23
+ const imagePath = join(IMAGE_DIR, `${Date.now()}-${safeId.slice(-8)}.jpg`);
24
+ await writeFile(imagePath, buffer);
25
+ return imagePath;
26
+ }
27
+ export function setupTelegramHandlers(bot, config, sessionManager) {
28
+ const accessControl = new AccessControl(config.allowedUserIds);
29
+ const requestQueue = new RequestQueue();
30
+ const userCosts = new Map();
31
+ const runningTasks = new Map();
32
+ const stopTaskCleanup = startTaskCleanup(runningTasks);
33
+ const dedup = new MessageDedup();
34
+ const commandHandler = new CommandHandler({
35
+ config,
36
+ sessionManager,
37
+ requestQueue,
38
+ sender: { sendTextReply },
39
+ userCosts,
40
+ getRunningTasksSize: () => runningTasks.size,
41
+ });
42
+ registerPermissionSender('telegram', {});
43
+ async function handleAIRequest(userId, chatId, prompt, workDir, convId, _threadCtx, replyToMessageId) {
44
+ const toolAdapter = getAdapter(config.aiCommand);
45
+ if (!toolAdapter) {
46
+ await sendTextReply(chatId, `ęœŖé…ē½® AI å·„å…·: ${config.aiCommand}`);
47
+ return;
48
+ }
49
+ const sessionId = convId ? sessionManager.getSessionIdForConv(userId, convId) : undefined;
50
+ log.info(`Running ${config.aiCommand} for user ${userId}, sessionId=${sessionId ?? 'new'}`);
51
+ const toolId = config.aiCommand;
52
+ let msgId;
53
+ try {
54
+ msgId = await sendThinkingMessage(chatId, replyToMessageId, toolId);
55
+ }
56
+ catch (err) {
57
+ log.error('Failed to send thinking message:', err);
58
+ return;
59
+ }
60
+ const stopTyping = startTypingLoop(chatId);
61
+ const taskKey = `${userId}:${msgId}`;
62
+ await runAITask({ config, sessionManager, userCosts }, { userId, chatId, workDir, sessionId, convId, platform: 'telegram', taskKey }, prompt, toolAdapter, {
63
+ throttleMs: THROTTLE_MS,
64
+ streamUpdate: (content, toolNote) => {
65
+ const note = toolNote ? '输出中...\n' + toolNote : '输出中...';
66
+ updateMessage(chatId, msgId, content, 'streaming', note, toolId).catch(() => { });
67
+ },
68
+ sendComplete: async (content, note) => {
69
+ await sendFinalMessages(chatId, msgId, content, note, toolId);
70
+ },
71
+ sendError: async (error) => {
72
+ await updateMessage(chatId, msgId, `é”™čÆÆļ¼š${error}`, 'error', 'ę‰§č”Œå¤±č“„', toolId);
73
+ },
74
+ extraCleanup: () => {
75
+ stopTyping();
76
+ runningTasks.delete(taskKey);
77
+ },
78
+ onTaskReady: (state) => {
79
+ runningTasks.set(taskKey, state);
80
+ },
81
+ sendImage: (path) => sendImageReply(chatId, path),
82
+ });
83
+ }
84
+ bot.on('callback_query', async (ctx) => {
85
+ const query = ctx.callbackQuery;
86
+ if (!('data' in query))
87
+ return;
88
+ const userId = String(ctx.from?.id ?? '');
89
+ const data = query.data;
90
+ if (data.startsWith('stop_')) {
91
+ const messageId = data.replace('stop_', '');
92
+ const taskKey = `${userId}:${messageId}`;
93
+ const taskInfo = runningTasks.get(taskKey);
94
+ if (taskInfo) {
95
+ runningTasks.delete(taskKey);
96
+ taskInfo.settle();
97
+ taskInfo.handle.abort();
98
+ const chatId = String(ctx.chat?.id ?? '');
99
+ await updateMessage(chatId, messageId, taskInfo.latestContent || '已停止', 'error', 'ā¹ļø 已停止', config.aiCommand);
100
+ await ctx.answerCbQuery('å·²åœę­¢ę‰§č”Œ');
101
+ }
102
+ else {
103
+ await ctx.answerCbQuery('ä»»åŠ”å·²å®Œęˆęˆ–äøå­˜åœØ');
104
+ }
105
+ }
106
+ else if (data.startsWith('perm_allow_') || data.startsWith('perm_deny_')) {
107
+ const isAllow = data.startsWith('perm_allow_');
108
+ const requestId = data.replace(/^perm_(allow|deny)_/, '');
109
+ const decision = isAllow ? 'allow' : 'deny';
110
+ resolvePermissionById(requestId, decision);
111
+ await ctx.answerCbQuery(isAllow ? 'āœ… 已允许' : 'āŒ å·²ę‹’ē»');
112
+ }
113
+ });
114
+ bot.on(message('text'), async (ctx) => {
115
+ const chatId = String(ctx.chat.id);
116
+ const userId = String(ctx.from.id);
117
+ const messageId = String(ctx.message.message_id);
118
+ let text = ctx.message.text.trim();
119
+ if (dedup.isDuplicate(`${chatId}:${messageId}`))
120
+ return;
121
+ if (!accessControl.isAllowed(userId)) {
122
+ await sendTextReply(chatId, 'ęŠ±ę­‰ļ¼Œę‚Øę²”ęœ‰č®æé—®ęƒé™ć€‚\nę‚Øēš„ ID: ' + userId);
123
+ return;
124
+ }
125
+ setActiveChatId('telegram', chatId);
126
+ if (await commandHandler.dispatch(text, chatId, userId, 'telegram', handleAIRequest)) {
127
+ return;
128
+ }
129
+ const workDir = sessionManager.getWorkDir(userId);
130
+ const convId = sessionManager.getConvId(userId);
131
+ const enqueueResult = requestQueue.enqueue(userId, convId, text, async (prompt) => {
132
+ await handleAIRequest(userId, chatId, prompt, workDir, convId, undefined, messageId);
133
+ });
134
+ if (enqueueResult === 'rejected') {
135
+ await sendTextReply(chatId, 'čÆ·ę±‚é˜Ÿåˆ—å·²ę»”ļ¼ŒčÆ·ēØåŽå†čÆ•ć€‚');
136
+ }
137
+ else if (enqueueResult === 'queued') {
138
+ await sendTextReply(chatId, 'ę‚Øēš„čÆ·ę±‚å·²ęŽ’é˜Ÿē­‰å¾…ć€‚');
139
+ }
140
+ });
141
+ bot.on(message('photo'), async (ctx) => {
142
+ const chatId = String(ctx.chat.id);
143
+ const userId = String(ctx.from.id);
144
+ const caption = ctx.message.caption?.trim() || '';
145
+ if (dedup.isDuplicate(`${chatId}:${ctx.message.message_id}`))
146
+ return;
147
+ if (!accessControl.isAllowed(userId))
148
+ return;
149
+ setActiveChatId('telegram', chatId);
150
+ const photos = ctx.message.photo;
151
+ const largest = photos[photos.length - 1];
152
+ let imagePath;
153
+ try {
154
+ imagePath = await downloadTelegramPhoto(bot, largest.file_id);
155
+ }
156
+ catch (err) {
157
+ log.error('Failed to download photo:', err);
158
+ await sendTextReply(chatId, '图片下载失蓄。');
159
+ return;
160
+ }
161
+ const prompt = caption
162
+ ? `ē”Øęˆ·å‘é€äŗ†äø€å¼ å›¾ē‰‡ļ¼ˆé™„čØ€ļ¼š${caption}ļ¼‰ļ¼Œå·²äæå­˜åˆ° ${imagePath}。请用 Read å·„å…·ęŸ„ēœ‹å¹¶åˆ†ęžć€‚`
163
+ : `ē”Øęˆ·å‘é€äŗ†äø€å¼ å›¾ē‰‡ļ¼Œå·²äæå­˜åˆ° ${imagePath}。请用 Read å·„å…·ęŸ„ēœ‹å¹¶åˆ†ęžć€‚`;
164
+ const workDir = sessionManager.getWorkDir(userId);
165
+ const convId = sessionManager.getConvId(userId);
166
+ requestQueue.enqueue(userId, convId, prompt, async (p) => {
167
+ await handleAIRequest(userId, chatId, p, workDir, convId);
168
+ });
169
+ });
170
+ return {
171
+ stop: () => stopTaskCleanup(),
172
+ getRunningTaskCount: () => runningTasks.size,
173
+ };
174
+ }
@@ -0,0 +1,7 @@
1
+ export type MessageStatus = 'thinking' | 'streaming' | 'done' | 'error';
2
+ export declare function sendThinkingMessage(chatId: string, replyToMessageId?: string, toolId?: string): Promise<string>;
3
+ export declare function updateMessage(chatId: string, messageId: string, content: string, status: MessageStatus, note?: string, toolId?: string): Promise<void>;
4
+ export declare function sendFinalMessages(chatId: string, messageId: string, fullContent: string, note: string, toolId?: string): Promise<void>;
5
+ export declare function sendTextReply(chatId: string, text: string): Promise<void>;
6
+ export declare function sendImageReply(chatId: string, imagePath: string): Promise<void>;
7
+ export declare function startTypingLoop(chatId: string): () => void;
@@ -0,0 +1,102 @@
1
+ import { getBot } from './client.js';
2
+ import { createReadStream } from 'node:fs';
3
+ import { createLogger } from '../logger.js';
4
+ import { splitLongContent, truncateText } from '../shared/utils.js';
5
+ import { MAX_TELEGRAM_MESSAGE_LENGTH } from '../constants.js';
6
+ const log = createLogger('TgSender');
7
+ const STATUS_ICONS = {
8
+ thinking: 'šŸ”µ',
9
+ streaming: 'šŸ”µ',
10
+ done: '🟢',
11
+ error: 'šŸ”“',
12
+ };
13
+ const TOOL_DISPLAY_NAMES = {
14
+ claude: 'claude-code',
15
+ codex: 'codex',
16
+ cursor: 'cursor',
17
+ };
18
+ function getToolTitle(toolId, status) {
19
+ const name = TOOL_DISPLAY_NAMES[toolId] ?? toolId;
20
+ if (status === 'thinking')
21
+ return `${name} - ę€č€ƒäø­...`;
22
+ if (status === 'error')
23
+ return `${name} - 错误`;
24
+ return name;
25
+ }
26
+ function formatMessage(content, status, note, toolId = 'claude') {
27
+ const icon = STATUS_ICONS[status];
28
+ const title = getToolTitle(toolId, status);
29
+ const text = truncateText(content, MAX_TELEGRAM_MESSAGE_LENGTH);
30
+ let out = `${icon} ${title}\n\n${text}`;
31
+ if (note)
32
+ out += `\n\n─────────\n${note}`;
33
+ return out;
34
+ }
35
+ function buildStopKeyboard(messageId) {
36
+ return {
37
+ inline_keyboard: [[{ text: 'ā¹ļø 停止', callback_data: `stop_${messageId}` }]],
38
+ };
39
+ }
40
+ export async function sendThinkingMessage(chatId, replyToMessageId, toolId = 'claude') {
41
+ const bot = getBot();
42
+ const extra = {};
43
+ if (replyToMessageId) {
44
+ extra.reply_parameters = {
45
+ message_id: Number(replyToMessageId),
46
+ };
47
+ }
48
+ const msg = await bot.telegram.sendMessage(Number(chatId), formatMessage('ę­£åœØę€č€ƒ...', 'thinking', 'čÆ·ēØå€™', toolId), { ...extra, parse_mode: 'Markdown' });
49
+ await bot.telegram.editMessageText(Number(chatId), msg.message_id, undefined, formatMessage('ę­£åœØę€č€ƒ...', 'thinking', 'čÆ·ēØå€™', toolId), { reply_markup: buildStopKeyboard(msg.message_id), parse_mode: 'Markdown' });
50
+ return String(msg.message_id);
51
+ }
52
+ export async function updateMessage(chatId, messageId, content, status, note, toolId = 'claude') {
53
+ const bot = getBot();
54
+ const opts = {};
55
+ if (status === 'thinking' || status === 'streaming') {
56
+ opts.reply_markup = buildStopKeyboard(Number(messageId));
57
+ }
58
+ try {
59
+ await bot.telegram.editMessageText(Number(chatId), Number(messageId), undefined, formatMessage(content, status, note, toolId), { ...opts, parse_mode: 'Markdown' });
60
+ }
61
+ catch (err) {
62
+ if (err && typeof err === 'object' && 'message' in err && String(err.message).includes('not modified')) {
63
+ /* ignore */
64
+ }
65
+ else {
66
+ log.error('Failed to update message:', err);
67
+ }
68
+ }
69
+ }
70
+ export async function sendFinalMessages(chatId, messageId, fullContent, note, toolId = 'claude') {
71
+ const parts = splitLongContent(fullContent, MAX_TELEGRAM_MESSAGE_LENGTH);
72
+ await updateMessage(chatId, messageId, parts[0], 'done', note, toolId);
73
+ const bot = getBot();
74
+ for (let i = 1; i < parts.length; i++) {
75
+ try {
76
+ await bot.telegram.sendMessage(Number(chatId), formatMessage(parts[i], 'done', `(ē»­ ${i + 1}/${parts.length}) ${note}`, toolId), { parse_mode: 'Markdown' });
77
+ }
78
+ catch (err) {
79
+ log.error('Failed to send continuation:', err);
80
+ }
81
+ }
82
+ }
83
+ export async function sendTextReply(chatId, text) {
84
+ const bot = getBot();
85
+ try {
86
+ await bot.telegram.sendMessage(Number(chatId), text, { parse_mode: 'Markdown' });
87
+ }
88
+ catch (err) {
89
+ log.error('Failed to send text:', err);
90
+ }
91
+ }
92
+ export async function sendImageReply(chatId, imagePath) {
93
+ const bot = getBot();
94
+ await bot.telegram.sendPhoto(Number(chatId), { source: createReadStream(imagePath) });
95
+ }
96
+ export function startTypingLoop(chatId) {
97
+ const bot = getBot();
98
+ const interval = setInterval(() => {
99
+ bot.telegram.sendChatAction(Number(chatId), 'typing').catch(() => { });
100
+ }, 4000);
101
+ return () => clearInterval(interval);
102
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@wu529778790/open-im",
3
+ "version": "0.1.0",
4
+ "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, Cursor)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "open-im": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ ".env.example"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsx src/index.ts",
17
+ "start": "node dist/index.js",
18
+ "setup": "node dist/index.js --setup-only",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "telegram",
23
+ "feishu",
24
+ "claude",
25
+ "claude-code",
26
+ "bot",
27
+ "bridge"
28
+ ],
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/wu529778790/open-im.git"
33
+ },
34
+ "homepage": "https://github.com/wu529778790/open-im#readme",
35
+ "bugs": {
36
+ "url": "https://github.com/wu529778790/open-im/issues"
37
+ },
38
+ "dependencies": {
39
+ "prompts": "^2.4.2",
40
+ "telegraf": "^4.16.3"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.0.0",
44
+ "@types/prompts": "^2.4.9",
45
+ "dotenv": "^16.0.0",
46
+ "tsx": "^4.0.0",
47
+ "typescript": "^5.0.0"
48
+ },
49
+ "engines": {
50
+ "node": ">=20"
51
+ }
52
+ }