@wu529778790/open-im 0.2.8 → 0.2.9

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
@@ -81,6 +81,7 @@ open-im run
81
81
  | `open-im init` | 初始化配置(首次使用或重新配置) |
82
82
  | `open-im start` | 后台启动服务 |
83
83
  | `open-im stop` | 停止服务 |
84
+ | `open-im restart` | 重启服务 |
84
85
 
85
86
  ### Telegram 机器人命令
86
87
 
package/dist/cli.js CHANGED
@@ -129,24 +129,8 @@ async function stopService() {
129
129
  await removePidFile();
130
130
  }
131
131
  }
132
- const args = process.argv.slice(2);
133
- if (args[0] === 'init') {
134
- // 手动触发配置
135
- console.log('\n━━━ open-im 配置向导 ━━━\n');
136
- const saved = await runInteractiveSetup();
137
- if (!saved) {
138
- console.log('配置未完成。');
139
- process.exit(1);
140
- }
141
- console.log('\n✅ 配置完成!现在可以运行以下命令启动服务:\n open-im start\n');
142
- }
143
- else if (args[0] === 'stop') {
144
- stopService().catch((err) => {
145
- console.error('停止服务时出错:', err);
146
- process.exit(1);
147
- });
148
- }
149
- else if (args[0] === 'start') {
132
+ // 启动服务(后台)
133
+ async function startService() {
150
134
  // 首先检查是否需要配置
151
135
  if (needsSetup()) {
152
136
  console.log('\n━━━ open-im 首次配置 ━━━\n');
@@ -186,6 +170,36 @@ else if (args[0] === 'start') {
186
170
  child.unref();
187
171
  console.log(`服务已在后台启动 (PID: ${child.pid})`);
188
172
  }
173
+ const args = process.argv.slice(2);
174
+ if (args[0] === 'init') {
175
+ // 手动触发配置
176
+ console.log('\n━━━ open-im 配置向导 ━━━\n');
177
+ const saved = await runInteractiveSetup();
178
+ if (!saved) {
179
+ console.log('配置未完成。');
180
+ process.exit(1);
181
+ }
182
+ console.log('\n✅ 配置完成!现在可以运行以下命令启动服务:\n open-im start\n');
183
+ }
184
+ else if (args[0] === 'stop') {
185
+ stopService().catch((err) => {
186
+ console.error('停止服务时出错:', err);
187
+ process.exit(1);
188
+ });
189
+ }
190
+ else if (args[0] === 'restart') {
191
+ console.log('正在重启服务...\n');
192
+ await stopService().catch((err) => {
193
+ console.error('停止服务时出错:', err);
194
+ });
195
+ // 等待进程完全退出
196
+ await new Promise(resolve => setTimeout(resolve, 1000));
197
+ console.log('\n正在重新启动服务...\n');
198
+ await startService();
199
+ }
200
+ else if (args[0] === 'start') {
201
+ await startService();
202
+ }
189
203
  else if (args[0] === 'run' || args.length === 0) {
190
204
  // 前台运行(默认命令)
191
205
  console.log('\n🚀 正在前台启动 open-im 服务...\n');
@@ -1,8 +1,8 @@
1
1
  import type { Config } from '../config.js';
2
2
  import type { SessionManager } from '../session/session-manager.js';
3
3
  import type { RequestQueue } from '../queue/request-queue.js';
4
- import type { ThreadContext, CostRecord } from '../shared/types.js';
5
- export type { ThreadContext, CostRecord };
4
+ import type { ThreadContext } from '../shared/types.js';
5
+ export type { ThreadContext };
6
6
  export interface MessageSender {
7
7
  sendTextReply(chatId: string, text: string, threadCtx?: ThreadContext): Promise<void>;
8
8
  }
@@ -11,7 +11,6 @@ export interface CommandHandlerDeps {
11
11
  sessionManager: SessionManager;
12
12
  requestQueue: RequestQueue;
13
13
  sender: MessageSender;
14
- userCosts: Map<string, CostRecord>;
15
14
  getRunningTasksSize: () => number;
16
15
  }
17
16
  export type ClaudeRequestHandler = (userId: string, chatId: string, prompt: string, workDir: string, convId?: string, threadCtx?: ThreadContext, replyToMessageId?: string) => Promise<void>;
@@ -69,7 +69,6 @@ export class CommandHandler {
69
69
  const workDir = this.deps.sessionManager.getWorkDir(userId);
70
70
  const convId = this.deps.sessionManager.getConvId(userId);
71
71
  const sessionId = this.deps.sessionManager.getSessionIdForConv(userId, convId);
72
- const record = this.deps.userCosts.get(userId);
73
72
  const lines = [
74
73
  '📊 状态:',
75
74
  '',
@@ -77,7 +76,6 @@ export class CommandHandler {
77
76
  `版本: ${version}`,
78
77
  `工作目录: ${workDir}`,
79
78
  `会话: ${sessionId ?? '无'}`,
80
- `费用: $${record?.totalCost.toFixed(4) ?? '0.0000'}`,
81
79
  ];
82
80
  await this.deps.sender.sendTextReply(chatId, lines.join('\n'));
83
81
  return true;
@@ -4,11 +4,9 @@
4
4
  import type { Config } from '../config.js';
5
5
  import type { SessionManager } from '../session/session-manager.js';
6
6
  import type { ToolAdapter } from '../adapters/tool-adapter.interface.js';
7
- import type { CostRecord } from './types.js';
8
7
  export interface TaskDeps {
9
8
  config: Config;
10
9
  sessionManager: SessionManager;
11
- userCosts: Map<string, CostRecord>;
12
10
  }
13
11
  export interface TaskContext {
14
12
  userId: string;
@@ -1,19 +1,13 @@
1
1
  /**
2
2
  * 共享 AI 任务执行层 - 支持多 ToolAdapter
3
3
  */
4
- import { formatToolStats, formatToolCallNotification, trackCost, getContextWarning, } from './utils.js';
4
+ import { formatToolStats, formatToolCallNotification, getContextWarning, } from './utils.js';
5
5
  import { createLogger } from '../logger.js';
6
6
  const log = createLogger('AITask');
7
7
  function buildCompletionNote(result, sessionManager, ctx) {
8
8
  const toolInfo = formatToolStats(result.toolStats, result.numTurns);
9
9
  const parts = [];
10
- if (result.cost > 0) {
11
- parts.push(`耗时 ${(result.durationMs / 1000).toFixed(1)}s`);
12
- parts.push(`费用 $${result.cost.toFixed(4)}`);
13
- }
14
- else {
15
- parts.push('完成');
16
- }
10
+ parts.push(`耗时 ${(result.durationMs / 1000).toFixed(1)}s`);
17
11
  if (toolInfo)
18
12
  parts.push(toolInfo);
19
13
  if (result.model)
@@ -27,7 +21,7 @@ function buildCompletionNote(result, sessionManager, ctx) {
27
21
  return parts.join(' | ');
28
22
  }
29
23
  export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
30
- const { config, sessionManager, userCosts } = deps;
24
+ const { config, sessionManager } = deps;
31
25
  return new Promise((resolve) => {
32
26
  let lastUpdateTime = 0;
33
27
  let pendingUpdate = null;
@@ -125,8 +119,6 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
125
119
  pendingUpdate = null;
126
120
  }
127
121
  const note = buildCompletionNote(result, sessionManager, ctx);
128
- log.info(`Task completed for user ${ctx.userId}: cost=$${result.cost.toFixed(4)}`);
129
- trackCost(userCosts, ctx.userId, result.cost, result.durationMs);
130
122
  const finalContent = result.accumulated || result.result || '(无输出)';
131
123
  try {
132
124
  await platformAdapter.sendComplete(finalContent, note, thinkingText || undefined);
@@ -2,8 +2,3 @@ export interface ThreadContext {
2
2
  rootMessageId: string;
3
3
  threadId: string;
4
4
  }
5
- export interface CostRecord {
6
- totalCost: number;
7
- totalDurationMs: number;
8
- requestCount: number;
9
- }
@@ -1,7 +1,5 @@
1
- import type { CostRecord } from './types.js';
2
1
  export declare function truncateText(text: string, maxLen: number): string;
3
2
  export declare function splitLongContent(text: string, maxLen: number): string[];
4
3
  export declare function formatToolStats(toolStats: Record<string, number>, numTurns: number): string;
5
4
  export declare function formatToolCallNotification(toolName: string, toolInput?: Record<string, unknown>): string;
6
- export declare function trackCost(userCosts: Map<string, CostRecord>, userId: string, cost: number, durationMs: number): void;
7
5
  export declare function getContextWarning(totalTurns: number): string | null;
@@ -56,13 +56,6 @@ export function formatToolCallNotification(toolName, toolInput) {
56
56
  detail = ` → ${toolInput.file_path}`;
57
57
  return `${emoji} ${toolName}${detail}`;
58
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
59
  export function getContextWarning(totalTurns) {
67
60
  if (totalTurns >= 12)
68
61
  return '⚠️ 上下文较长,建议 /new 开始新会话';
@@ -27,7 +27,6 @@ async function downloadTelegramPhoto(bot, fileId) {
27
27
  export function setupTelegramHandlers(bot, config, sessionManager) {
28
28
  const accessControl = new AccessControl(config.allowedUserIds);
29
29
  const requestQueue = new RequestQueue();
30
- const userCosts = new Map();
31
30
  const runningTasks = new Map();
32
31
  const stopTaskCleanup = startTaskCleanup(runningTasks);
33
32
  const dedup = new MessageDedup();
@@ -36,7 +35,6 @@ export function setupTelegramHandlers(bot, config, sessionManager) {
36
35
  sessionManager,
37
36
  requestQueue,
38
37
  sender: { sendTextReply },
39
- userCosts,
40
38
  getRunningTasksSize: () => runningTasks.size,
41
39
  });
42
40
  registerPermissionSender('telegram', {});
@@ -59,7 +57,7 @@ export function setupTelegramHandlers(bot, config, sessionManager) {
59
57
  }
60
58
  const stopTyping = startTypingLoop(chatId);
61
59
  const taskKey = `${userId}:${msgId}`;
62
- await runAITask({ config, sessionManager, userCosts }, { userId, chatId, workDir, sessionId, convId, platform: 'telegram', taskKey }, prompt, toolAdapter, {
60
+ await runAITask({ config, sessionManager }, { userId, chatId, workDir, sessionId, convId, platform: 'telegram', taskKey }, prompt, toolAdapter, {
63
61
  throttleMs: THROTTLE_MS,
64
62
  streamUpdate: (content, toolNote) => {
65
63
  const note = toolNote ? '输出中...\n' + toolNote : '输出中...';
@@ -45,8 +45,9 @@ export async function sendThinkingMessage(chatId, replyToMessageId, toolId = 'cl
45
45
  message_id: Number(replyToMessageId),
46
46
  };
47
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' });
48
+ // 初始消息使用纯文本,避免 Markdown 解析问题
49
+ const msg = await bot.telegram.sendMessage(Number(chatId), formatMessage('正在思考...', 'thinking', '请稍候', toolId), extra);
50
+ await bot.telegram.editMessageText(Number(chatId), msg.message_id, undefined, formatMessage('正在思考...', 'thinking', '请稍候', toolId), { reply_markup: buildStopKeyboard(msg.message_id) });
50
51
  return String(msg.message_id);
51
52
  }
52
53
  export async function updateMessage(chatId, messageId, content, status, note, toolId = 'claude') {
@@ -55,8 +56,11 @@ export async function updateMessage(chatId, messageId, content, status, note, to
55
56
  if (status === 'thinking' || status === 'streaming') {
56
57
  opts.reply_markup = buildStopKeyboard(Number(messageId));
57
58
  }
59
+ // 流式输出时使用纯文本,避免 Markdown 解析导致内容减少
60
+ // 只在完成时应用 Markdown 格式
61
+ const shouldParseMarkdown = status === 'done' || status === 'error';
58
62
  try {
59
- await bot.telegram.editMessageText(Number(chatId), Number(messageId), undefined, formatMessage(content, status, note, toolId), { ...opts, parse_mode: 'Markdown' });
63
+ await bot.telegram.editMessageText(Number(chatId), Number(messageId), undefined, formatMessage(content, status, note, toolId), { ...opts, parse_mode: shouldParseMarkdown ? 'Markdown' : undefined });
60
64
  }
61
65
  catch (err) {
62
66
  if (err && typeof err === 'object' && 'message' in err && String(err.message).includes('not modified')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, Cursor)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",