@wu529778790/open-im 0.1.1 → 0.1.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/.env.example +16 -16
- package/LICENSE +21 -21
- package/README.md +76 -76
- package/dist/claude/cli-runner.js +1 -1
- package/dist/cli.js +121 -16
- package/dist/index.js +38 -1
- package/dist/setup.js +6 -6
- package/package.json +54 -54
package/.env.example
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# Telegram Bot Token (from @BotFather)
|
|
2
|
-
TELEGRAM_BOT_TOKEN=
|
|
3
|
-
|
|
4
|
-
# Allowed user IDs (comma-separated, empty = allow all in dev)
|
|
5
|
-
ALLOWED_USER_IDS=
|
|
6
|
-
|
|
7
|
-
# AI tool: claude | codex | cursor
|
|
8
|
-
AI_COMMAND=claude
|
|
9
|
-
|
|
10
|
-
# Claude CLI (when AI_COMMAND=claude)
|
|
11
|
-
CLAUDE_CLI_PATH=claude
|
|
12
|
-
CLAUDE_WORK_DIR=.
|
|
13
|
-
CLAUDE_SKIP_PERMISSIONS=true
|
|
14
|
-
CLAUDE_TIMEOUT_MS=600000
|
|
15
|
-
CLAUDE_MODEL=
|
|
16
|
-
ALLOWED_BASE_DIRS=
|
|
1
|
+
# Telegram Bot Token (from @BotFather)
|
|
2
|
+
TELEGRAM_BOT_TOKEN=
|
|
3
|
+
|
|
4
|
+
# Allowed user IDs (comma-separated, empty = allow all in dev)
|
|
5
|
+
ALLOWED_USER_IDS=
|
|
6
|
+
|
|
7
|
+
# AI tool: claude | codex | cursor
|
|
8
|
+
AI_COMMAND=claude
|
|
9
|
+
|
|
10
|
+
# Claude CLI (when AI_COMMAND=claude)
|
|
11
|
+
CLAUDE_CLI_PATH=claude
|
|
12
|
+
CLAUDE_WORK_DIR=.
|
|
13
|
+
CLAUDE_SKIP_PERMISSIONS=true
|
|
14
|
+
CLAUDE_TIMEOUT_MS=600000
|
|
15
|
+
CLAUDE_MODEL=
|
|
16
|
+
ALLOWED_BASE_DIRS=
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 wu529778790
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wu529778790
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
# open-im
|
|
2
|
-
|
|
3
|
-
> 🚀 把你的 AI 助手装进口袋里 - 在 Telegram 随时随地使用 Claude Code
|
|
4
|
-
|
|
5
|
-
还在受限于终端吗?用手机也能 Coding 了!
|
|
6
|
-
|
|
7
|
-
open-im 是一个轻量级的 IM 桥接工具,让你通过 Telegram 就能使用 Claude Code、Codex、Cursor 等 AI CLI 工具。无论是在咖啡厅、地铁上,还是躺在床上,你的 AI 助手随时在线。
|
|
8
|
-
|
|
9
|
-
## ✨ 为什么选择 open-im
|
|
10
|
-
|
|
11
|
-
- **📱 移动友好** - 告别终端,用手机照样写代码
|
|
12
|
-
- **⚡ 实时流式输出** - AI 思考过程实时可见,像在终端一样流畅
|
|
13
|
-
- **🔒 安全可控** - 支持白名单,只有你能用
|
|
14
|
-
- **🔄 独立会话** - 每个人独立 session,互不干扰
|
|
15
|
-
- **🛠️ 多 AI 支持** - Claude / Codex / Cursor 随意切换
|
|
16
|
-
|
|
17
|
-
## 🚀 快速开始
|
|
18
|
-
|
|
19
|
-
### 方式一:npx(无需安装)
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npx @wu529778790/open-im start
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### 方式二:全局安装(推荐常用用户)
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm i @wu529778790/open-im -g
|
|
29
|
-
open-im start
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
首次运行会引导你完成配置,30 秒即可搞定。
|
|
33
|
-
|
|
34
|
-
## 📖 常用命令
|
|
35
|
-
|
|
36
|
-
| 命令 | 说明 |
|
|
37
|
-
|------|------|
|
|
38
|
-
| `open-im start` | 启动服务(后台运行) |
|
|
39
|
-
| `open-im stop` | 停止服务 |
|
|
40
|
-
|
|
41
|
-
### Telegram 机器人命令
|
|
42
|
-
|
|
43
|
-
| 命令 | 功能 |
|
|
44
|
-
|------|------|
|
|
45
|
-
| `/help` | 查看帮助 |
|
|
46
|
-
| `/new` | 开启新会话 |
|
|
47
|
-
| `/cd <路径>` | 切换工作目录 |
|
|
48
|
-
| `/pwd` | 查看当前目录 |
|
|
49
|
-
| `/status` | 查看运行状态 |
|
|
50
|
-
|
|
51
|
-
## 💡 使用场景
|
|
52
|
-
|
|
53
|
-
- 🚇 **通勤路上** - 用手机处理简单的代码问题
|
|
54
|
-
- ☕ **咖啡厅** - 没带电脑也能快速调试
|
|
55
|
-
- 🛋️ **沙发模式** - 躺着看 AI 帮你写代码
|
|
56
|
-
- 🌙 **紧急修复** - 半夜收到报警,手机直接处理
|
|
57
|
-
|
|
58
|
-
## 📦 安装方式
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# npx(无需安装)
|
|
62
|
-
npx @wu529778790/open-im start
|
|
63
|
-
|
|
64
|
-
# npm 全局安装
|
|
65
|
-
npm i @wu529778790/open-im -g
|
|
66
|
-
|
|
67
|
-
# yarn 全局安装
|
|
68
|
-
yarn global add @wu529778790/open-im
|
|
69
|
-
|
|
70
|
-
# pnpm 全局安装
|
|
71
|
-
pnpm i @wu529778790/open-im -g
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## 📝 License
|
|
75
|
-
|
|
76
|
-
[MIT](LICENSE)
|
|
1
|
+
# open-im
|
|
2
|
+
|
|
3
|
+
> 🚀 把你的 AI 助手装进口袋里 - 在 Telegram 随时随地使用 Claude Code
|
|
4
|
+
|
|
5
|
+
还在受限于终端吗?用手机也能 Coding 了!
|
|
6
|
+
|
|
7
|
+
open-im 是一个轻量级的 IM 桥接工具,让你通过 Telegram 就能使用 Claude Code、Codex、Cursor 等 AI CLI 工具。无论是在咖啡厅、地铁上,还是躺在床上,你的 AI 助手随时在线。
|
|
8
|
+
|
|
9
|
+
## ✨ 为什么选择 open-im
|
|
10
|
+
|
|
11
|
+
- **📱 移动友好** - 告别终端,用手机照样写代码
|
|
12
|
+
- **⚡ 实时流式输出** - AI 思考过程实时可见,像在终端一样流畅
|
|
13
|
+
- **🔒 安全可控** - 支持白名单,只有你能用
|
|
14
|
+
- **🔄 独立会话** - 每个人独立 session,互不干扰
|
|
15
|
+
- **🛠️ 多 AI 支持** - Claude / Codex / Cursor 随意切换
|
|
16
|
+
|
|
17
|
+
## 🚀 快速开始
|
|
18
|
+
|
|
19
|
+
### 方式一:npx(无需安装)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @wu529778790/open-im start
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 方式二:全局安装(推荐常用用户)
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm i @wu529778790/open-im -g
|
|
29
|
+
open-im start
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
首次运行会引导你完成配置,30 秒即可搞定。
|
|
33
|
+
|
|
34
|
+
## 📖 常用命令
|
|
35
|
+
|
|
36
|
+
| 命令 | 说明 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| `open-im start` | 启动服务(后台运行) |
|
|
39
|
+
| `open-im stop` | 停止服务 |
|
|
40
|
+
|
|
41
|
+
### Telegram 机器人命令
|
|
42
|
+
|
|
43
|
+
| 命令 | 功能 |
|
|
44
|
+
|------|------|
|
|
45
|
+
| `/help` | 查看帮助 |
|
|
46
|
+
| `/new` | 开启新会话 |
|
|
47
|
+
| `/cd <路径>` | 切换工作目录 |
|
|
48
|
+
| `/pwd` | 查看当前目录 |
|
|
49
|
+
| `/status` | 查看运行状态 |
|
|
50
|
+
|
|
51
|
+
## 💡 使用场景
|
|
52
|
+
|
|
53
|
+
- 🚇 **通勤路上** - 用手机处理简单的代码问题
|
|
54
|
+
- ☕ **咖啡厅** - 没带电脑也能快速调试
|
|
55
|
+
- 🛋️ **沙发模式** - 躺着看 AI 帮你写代码
|
|
56
|
+
- 🌙 **紧急修复** - 半夜收到报警,手机直接处理
|
|
57
|
+
|
|
58
|
+
## 📦 安装方式
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# npx(无需安装)
|
|
62
|
+
npx @wu529778790/open-im start
|
|
63
|
+
|
|
64
|
+
# npm 全局安装
|
|
65
|
+
npm i @wu529778790/open-im -g
|
|
66
|
+
|
|
67
|
+
# yarn 全局安装
|
|
68
|
+
yarn global add @wu529778790/open-im
|
|
69
|
+
|
|
70
|
+
# pnpm 全局安装
|
|
71
|
+
pnpm i @wu529778790/open-im -g
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 📝 License
|
|
75
|
+
|
|
76
|
+
[MIT](LICENSE)
|
|
@@ -22,7 +22,7 @@ export function runClaude(cliPath, prompt, sessionId, workDir, callbacks, option
|
|
|
22
22
|
env.CC_IM_CHAT_ID = options.chatId;
|
|
23
23
|
if (options?.hookPort)
|
|
24
24
|
env.CC_IM_HOOK_PORT = String(options.hookPort);
|
|
25
|
-
const child = spawn(cliPath, args, { cwd: workDir, stdio: ['ignore', 'pipe', 'pipe'], env });
|
|
25
|
+
const child = spawn(cliPath, args, { cwd: workDir, stdio: ['ignore', 'pipe', 'pipe'], env, windowsHide: true });
|
|
26
26
|
log.debug(`Claude CLI spawned: pid=${child.pid}, cwd=${workDir}, session=${sessionId ?? 'new'}`);
|
|
27
27
|
let accumulated = '';
|
|
28
28
|
let accumulatedThinking = '';
|
package/dist/cli.js
CHANGED
|
@@ -1,28 +1,133 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { main } from './index.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { mkdir, writeFile, rm, readFile } from 'node:fs/promises';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { platform } from 'node:os';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
// PID 文件路径
|
|
13
|
+
const PID_DIR = join(homedir(), '.open-im');
|
|
14
|
+
const PID_FILE = join(PID_DIR, 'daemon.pid');
|
|
15
|
+
const STOP_FILE = join(PID_DIR, 'stop.flag');
|
|
16
|
+
// 保存 PID 到文件
|
|
17
|
+
async function savePid(pid) {
|
|
18
|
+
try {
|
|
19
|
+
await mkdir(PID_DIR, { recursive: true });
|
|
20
|
+
await writeFile(PID_FILE, String(pid), 'utf-8');
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error('无法保存 PID 文件:', err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// 读取 PID 文件
|
|
27
|
+
async function readPid() {
|
|
28
|
+
try {
|
|
29
|
+
if (!existsSync(PID_FILE)) {
|
|
30
|
+
return null;
|
|
9
31
|
}
|
|
10
|
-
|
|
11
|
-
|
|
32
|
+
const content = await readFile(PID_FILE, 'utf-8');
|
|
33
|
+
return parseInt(content.trim(), 10);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// 删除 PID 文件
|
|
40
|
+
async function removePidFile() {
|
|
41
|
+
try {
|
|
42
|
+
if (existsSync(PID_FILE)) {
|
|
43
|
+
await rm(PID_FILE);
|
|
12
44
|
}
|
|
13
|
-
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// 忽略删除错误
|
|
48
|
+
}
|
|
14
49
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
50
|
+
// 检查进程是否在运行
|
|
51
|
+
async function isProcessRunning(pid) {
|
|
52
|
+
try {
|
|
53
|
+
// 尝试发送信号 0(不杀死进程,只检查是否存在)
|
|
54
|
+
process.kill(pid, 0);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 停止服务 - 创建停止标记文件,让服务优雅关闭
|
|
62
|
+
async function stopService() {
|
|
63
|
+
const pid = await readPid();
|
|
64
|
+
if (!pid) {
|
|
65
|
+
console.log('未找到运行中的服务(PID 文件不存在)');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const running = await isProcessRunning(pid);
|
|
69
|
+
if (!running) {
|
|
70
|
+
console.log(`服务未运行(进程 ${pid} 不存在)`);
|
|
71
|
+
await removePidFile();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
// 创建停止标记文件,服务会定期检查并优雅关闭
|
|
76
|
+
await mkdir(PID_DIR, { recursive: true });
|
|
77
|
+
await writeFile(STOP_FILE, Date.now().toString(), 'utf-8');
|
|
78
|
+
console.log('正在停止服务...');
|
|
79
|
+
// 等待进程退出(最多 10 秒)
|
|
80
|
+
const maxWait = 10000;
|
|
81
|
+
const interval = 200;
|
|
82
|
+
let waited = 0;
|
|
83
|
+
while (waited < maxWait) {
|
|
84
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
85
|
+
if (!(await isProcessRunning(pid))) {
|
|
86
|
+
await removePidFile();
|
|
87
|
+
console.log('服务已停止');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
waited += interval;
|
|
91
|
+
}
|
|
92
|
+
// 超时后强制终止
|
|
93
|
+
console.log('等待超时,强制终止服务...');
|
|
94
|
+
const isWindows = platform() === 'win32';
|
|
95
|
+
if (isWindows) {
|
|
96
|
+
execFileSync('taskkill', ['/F', '/PID', String(pid)], { stdio: 'ignore' });
|
|
21
97
|
}
|
|
22
98
|
else {
|
|
23
|
-
|
|
99
|
+
process.kill(pid, 'SIGKILL');
|
|
24
100
|
}
|
|
101
|
+
await removePidFile();
|
|
102
|
+
console.log('服务已强制停止');
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
106
|
+
console.error(`停止服务失败: ${errorMsg}`);
|
|
107
|
+
await removePidFile();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const args = process.argv.slice(2);
|
|
111
|
+
if (args[0] === 'stop') {
|
|
112
|
+
stopService().catch((err) => {
|
|
113
|
+
console.error('停止服务时出错:', err);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else if (args[0] === 'start') {
|
|
118
|
+
// 后台启动 - 跨平台方案
|
|
119
|
+
const distPath = join(__dirname, '..', 'dist', 'index.js');
|
|
120
|
+
// 使用 detached 模式创建独立进程
|
|
121
|
+
const child = spawn(process.execPath, [distPath], {
|
|
122
|
+
detached: true,
|
|
123
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
124
|
+
windowsHide: true // Windows 上隐藏控制台窗口
|
|
25
125
|
});
|
|
126
|
+
// 保存 PID
|
|
127
|
+
await savePid(child.pid);
|
|
128
|
+
// 让子进程独立于父进程
|
|
129
|
+
child.unref();
|
|
130
|
+
console.log(`服务已在后台启动 (PID: ${child.pid})`);
|
|
26
131
|
}
|
|
27
132
|
else {
|
|
28
133
|
// 默认启动(兼容直接运行 open-im)
|
package/dist/index.js
CHANGED
|
@@ -8,9 +8,30 @@ import { SessionManager } from './session/session-manager.js';
|
|
|
8
8
|
import { loadActiveChats, getActiveChatId, flushActiveChats } from './shared/active-chats.js';
|
|
9
9
|
import { initLogger, createLogger, closeLogger } from './logger.js';
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { rm } from 'node:fs/promises';
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
11
15
|
const require = createRequire(import.meta.url);
|
|
12
16
|
const { version: APP_VERSION } = require('../package.json');
|
|
13
17
|
const log = createLogger('Main');
|
|
18
|
+
// 停止标记文件路径
|
|
19
|
+
const STOP_FILE = join(homedir(), '.open-im', 'stop.flag');
|
|
20
|
+
// 检查是否收到停止信号
|
|
21
|
+
function checkStopSignal() {
|
|
22
|
+
return existsSync(STOP_FILE);
|
|
23
|
+
}
|
|
24
|
+
// 清理停止标记文件
|
|
25
|
+
async function clearStopSignal() {
|
|
26
|
+
try {
|
|
27
|
+
if (existsSync(STOP_FILE)) {
|
|
28
|
+
await rm(STOP_FILE);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// 忽略错误
|
|
33
|
+
}
|
|
34
|
+
}
|
|
14
35
|
async function sendLifecycleNotification(platform, message) {
|
|
15
36
|
const chatId = getActiveChatId('telegram');
|
|
16
37
|
if (!chatId)
|
|
@@ -55,7 +76,16 @@ export async function main() {
|
|
|
55
76
|
log.info('Shutting down...');
|
|
56
77
|
const uptimeSec = Math.floor((Date.now() - startedAt) / 1000);
|
|
57
78
|
const m = Math.floor(uptimeSec / 60);
|
|
58
|
-
|
|
79
|
+
try {
|
|
80
|
+
await sendLifecycleNotification('telegram', `🔴 open-im 服务正在关闭...\n运行时长: ${m}分钟`);
|
|
81
|
+
// 等待消息发送完成
|
|
82
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
log.debug('Failed to send shutdown notification:', err);
|
|
86
|
+
}
|
|
87
|
+
// 清理停止标记文件
|
|
88
|
+
await clearStopSignal();
|
|
59
89
|
telegramHandle?.stop();
|
|
60
90
|
stopTelegram();
|
|
61
91
|
sessionManager.destroy();
|
|
@@ -65,6 +95,13 @@ export async function main() {
|
|
|
65
95
|
};
|
|
66
96
|
process.on('SIGINT', () => shutdown().catch(() => process.exit(1)));
|
|
67
97
|
process.on('SIGTERM', () => shutdown().catch(() => process.exit(1)));
|
|
98
|
+
// 定期检查停止标记文件(用于 Windows 等无法发送信号的场景)
|
|
99
|
+
const stopCheckInterval = setInterval(() => {
|
|
100
|
+
if (checkStopSignal()) {
|
|
101
|
+
clearInterval(stopCheckInterval);
|
|
102
|
+
shutdown().catch(() => process.exit(1));
|
|
103
|
+
}
|
|
104
|
+
}, 1000);
|
|
68
105
|
}
|
|
69
106
|
const isEntry = process.argv[1]?.replace(/\\/g, '/').endsWith('/index.js') || process.argv[1]?.replace(/\\/g, '/').endsWith('/index.ts');
|
|
70
107
|
if (isEntry) {
|
package/dist/setup.js
CHANGED
|
@@ -14,12 +14,12 @@ function printManualInstructions(configPath) {
|
|
|
14
14
|
console.log(' 2. 创建文件:', configPath);
|
|
15
15
|
console.log(' 3. 填入以下内容(替换为你的 Token 和用户 ID):');
|
|
16
16
|
console.log('');
|
|
17
|
-
console.log(`{
|
|
18
|
-
"telegramBotToken": "你的Bot Token",
|
|
19
|
-
"allowedUserIds": ["你的Telegram用户ID"],
|
|
20
|
-
"claudeWorkDir": "${process.cwd().replace(/\\/g, '/')}",
|
|
21
|
-
"claudeSkipPermissions": true,
|
|
22
|
-
"aiCommand": "claude"
|
|
17
|
+
console.log(`{
|
|
18
|
+
"telegramBotToken": "你的Bot Token",
|
|
19
|
+
"allowedUserIds": ["你的Telegram用户ID"],
|
|
20
|
+
"claudeWorkDir": "${process.cwd().replace(/\\/g, '/')}",
|
|
21
|
+
"claudeSkipPermissions": true,
|
|
22
|
+
"aiCommand": "claude"
|
|
23
23
|
}`);
|
|
24
24
|
console.log('');
|
|
25
25
|
console.log('或设置环境变量: TELEGRAM_BOT_TOKEN=xxx 后再运行');
|
package/package.json
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@wu529778790/open-im",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
"run": "node dist/index.js",
|
|
18
|
-
"start": "node dist/
|
|
19
|
-
"stop": "
|
|
20
|
-
"setup": "node dist/index.js --setup-only",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
22
|
-
},
|
|
23
|
-
"keywords": [
|
|
24
|
-
"telegram",
|
|
25
|
-
"feishu",
|
|
26
|
-
"claude",
|
|
27
|
-
"claude-code",
|
|
28
|
-
"bot",
|
|
29
|
-
"bridge"
|
|
30
|
-
],
|
|
31
|
-
"license": "MIT",
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/wu529778790/open-im.git"
|
|
35
|
-
},
|
|
36
|
-
"homepage": "https://github.com/wu529778790/open-im#readme",
|
|
37
|
-
"bugs": {
|
|
38
|
-
"url": "https://github.com/wu529778790/open-im/issues"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"prompts": "^2.4.2",
|
|
42
|
-
"telegraf": "^4.16.3"
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/node": "^20.0.0",
|
|
46
|
-
"@types/prompts": "^2.4.9",
|
|
47
|
-
"dotenv": "^16.0.0",
|
|
48
|
-
"tsx": "^4.0.0",
|
|
49
|
-
"typescript": "^5.0.0"
|
|
50
|
-
},
|
|
51
|
-
"engines": {
|
|
52
|
-
"node": ">=20"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@wu529778790/open-im",
|
|
3
|
+
"version": "0.1.2",
|
|
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
|
+
"run": "node dist/index.js",
|
|
18
|
+
"start": "node dist/cli.js start",
|
|
19
|
+
"stop": "node dist/cli.js stop",
|
|
20
|
+
"setup": "node dist/index.js --setup-only",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"telegram",
|
|
25
|
+
"feishu",
|
|
26
|
+
"claude",
|
|
27
|
+
"claude-code",
|
|
28
|
+
"bot",
|
|
29
|
+
"bridge"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/wu529778790/open-im.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/wu529778790/open-im#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/wu529778790/open-im/issues"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"prompts": "^2.4.2",
|
|
42
|
+
"telegraf": "^4.16.3"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^20.0.0",
|
|
46
|
+
"@types/prompts": "^2.4.9",
|
|
47
|
+
"dotenv": "^16.0.0",
|
|
48
|
+
"tsx": "^4.0.0",
|
|
49
|
+
"typescript": "^5.0.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20"
|
|
53
|
+
}
|
|
54
|
+
}
|