@wu529778790/open-im 1.5.5-beta.8 → 1.6.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 +10 -3
- package/dist/cli.js +76 -168
- package/dist/config-web.d.ts +20 -0
- package/dist/config-web.js +1228 -0
- package/dist/config.d.ts +94 -2
- package/dist/config.js +8 -8
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/dingtalk/client.d.ts +1 -0
- package/dist/dingtalk/client.js +28 -0
- package/dist/dingtalk/client.test.d.ts +1 -0
- package/dist/dingtalk/client.test.js +22 -0
- package/dist/feishu/event-handler.js +18 -6
- package/dist/index.js +11 -11
- package/dist/manager-control.d.ts +17 -0
- package/dist/manager-control.js +160 -0
- package/dist/manager.d.ts +1 -0
- package/dist/manager.js +27 -0
- package/dist/service-control.d.ts +15 -0
- package/dist/service-control.js +131 -0
- package/dist/setup.js +3 -14
- package/dist/telegram/message-sender.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- 多平台:支持 Telegram、飞书、企业微信、钉钉、QQ、微信(测试中),可同时启用
|
|
8
8
|
- 多 AI 工具:支持 Claude、Codex、Cursor
|
|
9
9
|
- 流式输出:实时回传 AI 回复与工具执行进度(目前钉钉暂未实现流式传输)
|
|
10
|
+
- 图形化配置页面 / cli配置引导
|
|
10
11
|
- 会话隔离:每个用户独立维护本地会话,`/new` 可重置
|
|
11
12
|
- 常用命令:支持 `/help`、`/new`、`/cd`、`/pwd`、`/status`
|
|
12
13
|
|
|
@@ -40,6 +41,13 @@ open-im start
|
|
|
40
41
|
| `open-im stop` | 停止后台服务 |
|
|
41
42
|
| `open-im dev` | 前台运行(调试模式) |
|
|
42
43
|
|
|
44
|
+
## 图形化配置页面
|
|
45
|
+
|
|
46
|
+
- 默认地址:`http://127.0.0.1:39282`
|
|
47
|
+
- `open-im start` 会同时提供本地配置页面
|
|
48
|
+
- `open-im dev` 仅在未完成配置时自动打开页面
|
|
49
|
+
- 已有配置但想手动打开时,使用 `open-im start`
|
|
50
|
+
|
|
43
51
|
## 会话说明
|
|
44
52
|
|
|
45
53
|
会话上下文保存在本地 `~/.open-im/data/sessions.json`,与 IM 聊天记录本身无关。每个用户有独立会话目录和 session 信息,发送 `/new` 会重置当前 AI 会话。
|
|
@@ -110,8 +118,7 @@ Claude 默认使用 Agent SDK,不依赖本地 `claude` 可执行文件;通
|
|
|
110
118
|
"enabled": false,
|
|
111
119
|
"allowedUserIds": [],
|
|
112
120
|
"appId": "YOUR_QQ_APP_ID",
|
|
113
|
-
"secret": "YOUR_QQ_APP_SECRET"
|
|
114
|
-
"sandbox": false
|
|
121
|
+
"secret": "YOUR_QQ_APP_SECRET"
|
|
115
122
|
},
|
|
116
123
|
"wework": {
|
|
117
124
|
"enabled": false,
|
|
@@ -207,7 +214,7 @@ Claude 默认使用 Agent SDK,不依赖本地 `claude` 可执行文件;通
|
|
|
207
214
|
|
|
208
215
|
**Telegram 无响应**:检查网络,必要时在 Telegram 平台配置中添加 `"proxy": "http://127.0.0.1:7890"` 或设置 `TELEGRAM_PROXY`。
|
|
209
216
|
|
|
210
|
-
**QQ 无法连接**:确认机器人已在 QQ 开放平台创建并启用,检查 `QQ_BOT_APPID`、`QQ_BOT_SECRET` 或 `platforms.qq`
|
|
217
|
+
**QQ 无法连接**:确认机器人已在 QQ 开放平台创建并启用,检查 `QQ_BOT_APPID`、`QQ_BOT_SECRET` 或 `platforms.qq` 配置是否正确。
|
|
211
218
|
|
|
212
219
|
**QQ 重复回复**:如遇到消息重复发送,请确保使用最新版本,该问题已在近期修复。
|
|
213
220
|
|
package/dist/cli.js
CHANGED
|
@@ -1,222 +1,130 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn, execFileSync } from "node:child_process";
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs";
|
|
4
|
-
import { join, dirname } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
2
|
import { main, needsSetup, runInteractiveSetup } from "./index.js";
|
|
7
3
|
import { loadConfig } from "./config.js";
|
|
8
|
-
import { runPlatformSelectionPrompt } from "./setup.js";
|
|
9
4
|
import { checkAndUpdate } from "./check-update.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function getPid() {
|
|
19
|
-
if (!existsSync(PID_FILE))
|
|
20
|
-
return null;
|
|
21
|
-
try {
|
|
22
|
-
const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
23
|
-
return isNaN(pid) ? null : pid;
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function writePid(pid) {
|
|
30
|
-
try {
|
|
31
|
-
writeFileSync(PID_FILE, String(pid), "utf-8");
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
console.error("无法写入 PID 文件:", err);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function removePid() {
|
|
38
|
-
try {
|
|
39
|
-
if (existsSync(PID_FILE))
|
|
40
|
-
unlinkSync(PID_FILE);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
/* ignore */
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function isRunning(pid) {
|
|
47
|
-
try {
|
|
48
|
-
// Windows 下使用 tasklist 检查进程是否存在
|
|
49
|
-
if (process.platform === 'win32') {
|
|
50
|
-
const result = execFileSync('tasklist', ['/FI', `PID eq ${pid}`, '/NH'], {
|
|
51
|
-
stdio: 'pipe',
|
|
52
|
-
windowsHide: true
|
|
53
|
-
}).toString();
|
|
54
|
-
return result.includes(String(pid));
|
|
5
|
+
import { getWebConfigUrl, runWebConfigFlow } from "./config-web.js";
|
|
6
|
+
import { getManagerStatus, startManagerProcess, stopManagerProcess } from "./manager-control.js";
|
|
7
|
+
import { stopBackgroundService } from "./service-control.js";
|
|
8
|
+
async function ensureConfigured(mode) {
|
|
9
|
+
if (mode === "init") {
|
|
10
|
+
if (!process.stdin.isTTY) {
|
|
11
|
+
console.error("CLI setup requires an interactive terminal.");
|
|
12
|
+
return false;
|
|
55
13
|
}
|
|
56
|
-
// Unix 系统使用 kill 0 信号检查
|
|
57
|
-
process.kill(pid, 0);
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// 配置校验
|
|
66
|
-
// ============================================================================
|
|
67
|
-
async function validateOrSetup() {
|
|
68
|
-
if (needsSetup()) {
|
|
69
|
-
console.log("\n━━━ open-im 首次配置 ━━━\n");
|
|
70
|
-
console.log("检测到尚未配置,将先进入配置向导...\n");
|
|
71
14
|
const saved = await runInteractiveSetup();
|
|
72
|
-
if (!saved)
|
|
73
|
-
|
|
15
|
+
if (!saved)
|
|
16
|
+
return false;
|
|
17
|
+
try {
|
|
18
|
+
loadConfig();
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
74
23
|
return false;
|
|
75
24
|
}
|
|
76
|
-
console.log("");
|
|
77
25
|
}
|
|
26
|
+
if (!needsSetup()) {
|
|
27
|
+
try {
|
|
28
|
+
loadConfig();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const result = await runWebConfigFlow({ mode, cwd: process.cwd() });
|
|
36
|
+
if (result !== "saved")
|
|
37
|
+
return false;
|
|
78
38
|
try {
|
|
79
39
|
loadConfig();
|
|
80
40
|
return true;
|
|
81
41
|
}
|
|
82
|
-
catch (
|
|
83
|
-
|
|
84
|
-
console.error("配置无效或缺少必要字段:", msg);
|
|
85
|
-
console.log("\n请运行以下命令重新配置:\n npx @wu529778790/open-im init");
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
86
44
|
return false;
|
|
87
45
|
}
|
|
88
46
|
}
|
|
89
|
-
// ============================================================================
|
|
90
|
-
// 命令处理
|
|
91
|
-
// ============================================================================
|
|
92
47
|
async function cmdStart() {
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
console.log("\
|
|
96
|
-
console.log(`
|
|
48
|
+
const status = getManagerStatus();
|
|
49
|
+
if (status.running && status.pid) {
|
|
50
|
+
console.log("\nopen-im is already running in the background.");
|
|
51
|
+
console.log(` pid: ${status.pid}`);
|
|
52
|
+
console.log(` config page: ${getWebConfigUrl()}`);
|
|
97
53
|
return;
|
|
98
54
|
}
|
|
99
|
-
|
|
100
|
-
removePid(); // 清理可能存在的陈旧 PID 文件
|
|
101
|
-
}
|
|
102
|
-
if (!(await validateOrSetup())) {
|
|
55
|
+
if (!(await ensureConfigured("start"))) {
|
|
103
56
|
process.exit(1);
|
|
104
57
|
}
|
|
105
|
-
// 检查并自动更新到最新版本
|
|
106
58
|
const { updated } = await checkAndUpdate();
|
|
107
59
|
if (updated) {
|
|
108
60
|
process.exit(0);
|
|
109
61
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (!updated) {
|
|
115
|
-
console.log("已取消启动。");
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const child = spawn(process.execPath, [INDEX_JS], {
|
|
120
|
-
detached: true,
|
|
121
|
-
stdio: "ignore",
|
|
122
|
-
cwd: process.cwd(),
|
|
123
|
-
env: process.env,
|
|
124
|
-
windowsHide: process.platform === "win32",
|
|
125
|
-
});
|
|
126
|
-
child.unref();
|
|
127
|
-
writePid(child.pid);
|
|
128
|
-
console.log("\n🟢 open-im 已在后台启动");
|
|
129
|
-
console.log(` pid: ${child.pid}`);
|
|
62
|
+
const child = await startManagerProcess(process.cwd());
|
|
63
|
+
console.log("\nopen-im started in the background.");
|
|
64
|
+
console.log(` pid: ${child.pid}`);
|
|
65
|
+
console.log(` config page: ${getWebConfigUrl()}`);
|
|
130
66
|
}
|
|
131
67
|
async function cmdStop() {
|
|
132
|
-
const
|
|
133
|
-
if (!pid) {
|
|
134
|
-
console.log("open-im
|
|
68
|
+
const status = getManagerStatus();
|
|
69
|
+
if (!status.pid) {
|
|
70
|
+
console.log("open-im is not running in the background.");
|
|
135
71
|
return;
|
|
136
72
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
const port = existsSync(PORT_FILE)
|
|
143
|
-
? parseInt(readFileSync(PORT_FILE, "utf-8").trim(), 10) || SHUTDOWN_PORT
|
|
144
|
-
: SHUTDOWN_PORT;
|
|
145
|
-
try {
|
|
146
|
-
const res = await fetch(`http://127.0.0.1:${port}/shutdown`, {
|
|
147
|
-
signal: AbortSignal.timeout(3000),
|
|
148
|
-
});
|
|
149
|
-
if (res.ok) {
|
|
150
|
-
for (let i = 0; i < 50; i++) {
|
|
151
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
152
|
-
if (!isRunning(pid))
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
// HTTP 失败则用 SIGTERM 兜底
|
|
159
|
-
process.kill(pid, "SIGTERM");
|
|
160
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
161
|
-
}
|
|
162
|
-
if (isRunning(pid)) {
|
|
163
|
-
process.kill(pid, "SIGKILL");
|
|
164
|
-
}
|
|
165
|
-
removePid();
|
|
166
|
-
try {
|
|
167
|
-
if (existsSync(PORT_FILE))
|
|
168
|
-
unlinkSync(PORT_FILE);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
/* ignore */
|
|
172
|
-
}
|
|
173
|
-
console.log("\n🔴 open-im 已停止");
|
|
174
|
-
console.log(` pid: ${pid}`);
|
|
73
|
+
await stopBackgroundService();
|
|
74
|
+
const result = await stopManagerProcess();
|
|
75
|
+
console.log("\nopen-im stopped.");
|
|
76
|
+
console.log(` pid: ${result.pid}`);
|
|
175
77
|
}
|
|
176
78
|
async function cmdInit() {
|
|
177
|
-
console.log("\
|
|
178
|
-
const saved = await
|
|
179
|
-
if (saved) {
|
|
180
|
-
console.log("\
|
|
181
|
-
|
|
182
|
-
console.log(" open-im start # 后台运行");
|
|
183
|
-
console.log(" open-im dev # 前台运行(调试)");
|
|
79
|
+
console.log("\nopen-im CLI setup\n");
|
|
80
|
+
const saved = await ensureConfigured("init");
|
|
81
|
+
if (!saved) {
|
|
82
|
+
console.log("\nConfiguration was not completed.");
|
|
83
|
+
process.exit(1);
|
|
184
84
|
}
|
|
185
|
-
|
|
186
|
-
|
|
85
|
+
console.log("\nConfiguration saved.");
|
|
86
|
+
console.log("\nYou can start the app with:");
|
|
87
|
+
console.log(" open-im start");
|
|
88
|
+
console.log(" open-im dev");
|
|
89
|
+
}
|
|
90
|
+
async function cmdDev() {
|
|
91
|
+
if (!(await ensureConfigured("dev"))) {
|
|
92
|
+
console.log("Configuration was not completed.");
|
|
187
93
|
process.exit(1);
|
|
188
94
|
}
|
|
95
|
+
await main();
|
|
189
96
|
}
|
|
190
97
|
function showHelp(exitCode = 0) {
|
|
191
98
|
console.log(`
|
|
192
|
-
|
|
99
|
+
Usage: open-im <command>
|
|
100
|
+
|
|
101
|
+
Commands:
|
|
102
|
+
start Run the full app in the background and serve the local config page
|
|
103
|
+
stop Stop the full app
|
|
104
|
+
init Run CLI setup
|
|
105
|
+
dev Run in the foreground for debugging
|
|
193
106
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
init 配置向导(首次或追加配置,保留已有平台配置并更新 config.json)
|
|
198
|
-
dev 前台运行(调试模式),Ctrl+C 停止
|
|
107
|
+
Local config page:
|
|
108
|
+
http://127.0.0.1:39282
|
|
109
|
+
start keeps it available; dev opens it only during initial setup
|
|
199
110
|
|
|
200
|
-
|
|
201
|
-
-h, --help
|
|
111
|
+
Options:
|
|
112
|
+
-h, --help Show this help message
|
|
202
113
|
`);
|
|
203
114
|
process.exit(exitCode);
|
|
204
115
|
}
|
|
205
|
-
// ============================================================================
|
|
206
|
-
// 命令路由
|
|
207
|
-
// ============================================================================
|
|
208
116
|
const cmd = process.argv[2];
|
|
209
117
|
const commands = {
|
|
210
118
|
start: cmdStart,
|
|
211
119
|
stop: cmdStop,
|
|
212
120
|
init: cmdInit,
|
|
213
|
-
dev:
|
|
121
|
+
dev: cmdDev,
|
|
214
122
|
};
|
|
215
123
|
if (cmd === "--help" || cmd === "-h") {
|
|
216
124
|
showHelp(0);
|
|
217
125
|
}
|
|
218
126
|
else if (cmd === undefined) {
|
|
219
|
-
|
|
127
|
+
cmdDev().catch((err) => {
|
|
220
128
|
console.error(err);
|
|
221
129
|
process.exit(1);
|
|
222
130
|
});
|
|
@@ -228,6 +136,6 @@ else if (commands[cmd]) {
|
|
|
228
136
|
});
|
|
229
137
|
}
|
|
230
138
|
else {
|
|
231
|
-
console.error(
|
|
139
|
+
console.error(`Unknown command: ${cmd}`);
|
|
232
140
|
showHelp(1);
|
|
233
141
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type WebFlowMode = "init" | "start" | "dev";
|
|
2
|
+
type WebFlowResult = "saved" | "cancel";
|
|
3
|
+
export interface StartedWebConfigServer {
|
|
4
|
+
close: () => Promise<void>;
|
|
5
|
+
url: string;
|
|
6
|
+
waitForResult: Promise<WebFlowResult>;
|
|
7
|
+
}
|
|
8
|
+
export declare function getWebConfigPort(): number;
|
|
9
|
+
export declare function getWebConfigUrl(): string;
|
|
10
|
+
export declare function openWebConfigUrl(): void;
|
|
11
|
+
export declare function startWebConfigServer(options: {
|
|
12
|
+
mode: WebFlowMode;
|
|
13
|
+
cwd: string;
|
|
14
|
+
persistent?: boolean;
|
|
15
|
+
}): Promise<StartedWebConfigServer>;
|
|
16
|
+
export declare function runWebConfigFlow(options: {
|
|
17
|
+
mode: WebFlowMode;
|
|
18
|
+
cwd: string;
|
|
19
|
+
}): Promise<WebFlowResult>;
|
|
20
|
+
export {};
|