@wu529778790/open-im 1.1.0 → 1.1.1-beta.1
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 +9 -9
- package/dist/cli.js +12 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +15 -0
- package/dist/index.js +11 -3
- package/dist/setup.d.ts +7 -0
- package/dist/setup.js +71 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
|
-
- **多平台**:支持 Telegram
|
|
7
|
+
- **多平台**:支持 Telegram、飞书、企业微信、微信(测试中),可同时启用
|
|
8
8
|
- **多 AI 工具**:通过配置切换 Claude Code / Codex / Cursor
|
|
9
9
|
- **流式输出**:节流更新,实时展示 AI 回复
|
|
10
10
|
- **会话管理**:每用户独立 session,`/new` 重置会话
|
|
@@ -68,12 +68,12 @@ npm run dev # 直接运行源码(tsx,无需 build)
|
|
|
68
68
|
| `TELEGRAM_BOT_TOKEN` | Telegram Bot Token(从 @BotFather 获取) |
|
|
69
69
|
| `FEISHU_APP_ID` | 飞书应用 App ID |
|
|
70
70
|
| `FEISHU_APP_SECRET` | 飞书应用 App Secret |
|
|
71
|
-
| `WECHAT_APP_ID` | 微信应用 App ID(AGP 协议) |
|
|
72
|
-
| `WECHAT_APP_SECRET` | 微信应用 App Secret |
|
|
73
|
-
| `WECHAT_WS_URL` | AGP WebSocket URL(可选,默认使用官方服务) |
|
|
74
71
|
| `WEWORK_CORP_ID` | 企业微信机器人 ID(Bot ID) |
|
|
75
72
|
| `WEWORK_SECRET` | 企业微信机器人 Secret |
|
|
76
73
|
| `WEWORK_WS_URL` | 企业微信 WebSocket URL(可选,默认官方) |
|
|
74
|
+
| `WECHAT_APP_ID` | 微信应用 App ID(AGP/Qclaw 协议,测试中) |
|
|
75
|
+
| `WECHAT_APP_SECRET` | 微信应用 App Secret |
|
|
76
|
+
| `WECHAT_WS_URL` | AGP WebSocket URL(可选,默认使用官方服务) |
|
|
77
77
|
| `ALLOWED_USER_IDS` | 白名单用户 ID(逗号分隔,空=所有人) |
|
|
78
78
|
| `AI_COMMAND` | `claude` \| `codex` \| `cursor`,默认 `claude` |
|
|
79
79
|
| `CLAUDE_CLI_PATH` | Claude CLI 路径,默认 `claude` |
|
|
@@ -94,7 +94,7 @@ npm run dev # 直接运行源码(tsx,无需 build)
|
|
|
94
94
|
- **Telegram**:`TELEGRAM_BOT_TOKEN` 或 `telegramBotToken`
|
|
95
95
|
- **飞书**:`FEISHU_APP_ID` + `FEISHU_APP_SECRET` 或 `feishuAppId` + `feishuAppSecret`
|
|
96
96
|
- **企业微信**:`WEWORK_CORP_ID` + `WEWORK_SECRET` 或 `platforms.wework.corpId` + `platforms.wework.secret`
|
|
97
|
-
- **微信**:`WECHAT_APP_ID` + `WECHAT_APP_SECRET` 或 `wechatAppId` + `wechatAppSecret
|
|
97
|
+
- **微信**:`WECHAT_APP_ID` + `WECHAT_APP_SECRET` 或 `wechatAppId` + `wechatAppSecret`(测试中,基于 Qclaw 通道,连接可能不稳定)
|
|
98
98
|
|
|
99
99
|
### 飞书配置说明
|
|
100
100
|
|
|
@@ -290,10 +290,10 @@ open-im init
|
|
|
290
290
|
|
|
291
291
|
### Q: 微信连接失败(错误 1006 或 500)?
|
|
292
292
|
|
|
293
|
-
|
|
293
|
+
微信通道基于 **Qclaw** 接入,目前处于**测试阶段**,连接可能不稳定。若出现连接失败,可能原因:
|
|
294
294
|
|
|
295
|
-
1. **
|
|
296
|
-
-
|
|
295
|
+
1. **Qclaw 通道限制**
|
|
296
|
+
- Qclaw 通道可能有白名单限制
|
|
297
297
|
- 独立客户端可能不在允许列表中
|
|
298
298
|
- token 或 guid 无效或已过期
|
|
299
299
|
|
|
@@ -302,4 +302,4 @@ open-im init
|
|
|
302
302
|
- 尝试更新 token/guid(如果提供商支持)
|
|
303
303
|
- 或暂时使用其他平台(Telegram/飞书/企业微信)
|
|
304
304
|
|
|
305
|
-
|
|
305
|
+
**注意**:微信通道为测试功能,基于 Qclaw 接入,连接稳定性取决于第三方服务。建议优先使用 Telegram、飞书或企业微信。
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,8 @@ import { readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs";
|
|
|
4
4
|
import { join, dirname } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { main, needsSetup, runInteractiveSetup } from "./index.js";
|
|
7
|
-
import { loadConfig } from "./config.js";
|
|
7
|
+
import { loadConfig, getPlatformsWithCredentials } from "./config.js";
|
|
8
|
+
import { runPlatformSelectionPrompt } from "./setup.js";
|
|
8
9
|
import { APP_HOME, SHUTDOWN_PORT } from "./constants.js";
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const PID_FILE = join(APP_HOME, "open-im.pid");
|
|
@@ -88,6 +89,16 @@ async function cmdStart() {
|
|
|
88
89
|
if (!(await validateOrSetup())) {
|
|
89
90
|
process.exit(1);
|
|
90
91
|
}
|
|
92
|
+
// 多通道时在父进程(有 TTY)让用户选择,再启动子进程
|
|
93
|
+
let config = loadConfig();
|
|
94
|
+
if (getPlatformsWithCredentials(config).length > 1 &&
|
|
95
|
+
process.stdin.isTTY) {
|
|
96
|
+
const updated = await runPlatformSelectionPrompt(config);
|
|
97
|
+
if (!updated) {
|
|
98
|
+
console.log("已取消启动。");
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
91
102
|
const child = spawn(process.execPath, [INDEX_JS], {
|
|
92
103
|
detached: true,
|
|
93
104
|
stdio: "ignore",
|
package/dist/config.d.ts
CHANGED
|
@@ -62,3 +62,5 @@ export interface Config {
|
|
|
62
62
|
/** 检测是否需要交互式配置(无 token 且无环境变量) */
|
|
63
63
|
export declare function needsSetup(): boolean;
|
|
64
64
|
export declare function loadConfig(): Config;
|
|
65
|
+
/** 获取已配置凭证的平台列表(用于多通道启动时让用户选择),顺序:Telegram、飞书、企业微信、微信 */
|
|
66
|
+
export declare function getPlatformsWithCredentials(config: Config): Platform[];
|
package/dist/config.js
CHANGED
|
@@ -275,3 +275,18 @@ export function loadConfig() {
|
|
|
275
275
|
platforms,
|
|
276
276
|
};
|
|
277
277
|
}
|
|
278
|
+
/** 获取已配置凭证的平台列表(用于多通道启动时让用户选择),顺序:Telegram、飞书、企业微信、微信 */
|
|
279
|
+
export function getPlatformsWithCredentials(config) {
|
|
280
|
+
const r = [];
|
|
281
|
+
if (config.telegramBotToken)
|
|
282
|
+
r.push('telegram');
|
|
283
|
+
if (config.feishuAppId && config.feishuAppSecret)
|
|
284
|
+
r.push('feishu');
|
|
285
|
+
if (config.weworkCorpId && config.weworkSecret)
|
|
286
|
+
r.push('wework');
|
|
287
|
+
const hasWechat = (config.wechatToken && config.wechatGuid && config.wechatUserId) ||
|
|
288
|
+
(config.wechatAppId && config.wechatAppSecret);
|
|
289
|
+
if (hasWechat)
|
|
290
|
+
r.push('wechat');
|
|
291
|
+
return r;
|
|
292
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { loadConfig, needsSetup } from "./config.js";
|
|
5
|
-
import { runInteractiveSetup } from "./setup.js";
|
|
4
|
+
import { loadConfig, needsSetup, getPlatformsWithCredentials } from "./config.js";
|
|
5
|
+
import { runInteractiveSetup, runPlatformSelectionPrompt } from "./setup.js";
|
|
6
6
|
// 导出供 cli.ts 使用
|
|
7
7
|
export { needsSetup, runInteractiveSetup };
|
|
8
8
|
import { initTelegram, stopTelegram } from "./telegram/client.js";
|
|
@@ -62,7 +62,15 @@ export async function main() {
|
|
|
62
62
|
if (!saved)
|
|
63
63
|
process.exit(1);
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
let config = loadConfig();
|
|
66
|
+
// 多通道时让用户确认启用哪些(仅 TTY 交互模式)
|
|
67
|
+
if (getPlatformsWithCredentials(config).length > 1 &&
|
|
68
|
+
process.stdin.isTTY) {
|
|
69
|
+
const updated = await runPlatformSelectionPrompt(config);
|
|
70
|
+
if (!updated)
|
|
71
|
+
process.exit(0);
|
|
72
|
+
config = updated;
|
|
73
|
+
}
|
|
66
74
|
initLogger(config.logDir, config.logLevel);
|
|
67
75
|
loadActiveChats();
|
|
68
76
|
initPermissionModes();
|
package/dist/setup.d.ts
CHANGED
|
@@ -3,4 +3,11 @@
|
|
|
3
3
|
* 使用 prompts 库,兼容 tsx watch、IDE 终端等环境
|
|
4
4
|
* Telegram Token 使用 readline 避免 Windows 终端 prompts 重绘问题
|
|
5
5
|
*/
|
|
6
|
+
import type { Config } from "./config.js";
|
|
6
7
|
export declare function runInteractiveSetup(): Promise<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* 多通道启动时让用户确认/选择要启用的平台
|
|
10
|
+
* 仅当配置了 2 个及以上平台且 stdin 为 TTY 时调用
|
|
11
|
+
* @returns 更新后的 config,或 null 表示取消
|
|
12
|
+
*/
|
|
13
|
+
export declare function runPlatformSelectionPrompt(config: Config): Promise<Config | null>;
|
package/dist/setup.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createInterface } from "node:readline";
|
|
|
8
8
|
import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
9
9
|
import { join, dirname } from "node:path";
|
|
10
10
|
import { APP_HOME } from "./constants.js";
|
|
11
|
+
import { loadConfig, getPlatformsWithCredentials } from "./config.js";
|
|
11
12
|
function loadExistingConfig() {
|
|
12
13
|
const configPath = join(APP_HOME, "config.json");
|
|
13
14
|
if (!existsSync(configPath))
|
|
@@ -25,8 +26,8 @@ function getConfiguredPlatforms(existing) {
|
|
|
25
26
|
const names = [
|
|
26
27
|
{ k: "telegram", label: "Telegram" },
|
|
27
28
|
{ k: "feishu", label: "飞书" },
|
|
28
|
-
{ k: "wechat", label: "微信" },
|
|
29
29
|
{ k: "wework", label: "企业微信" },
|
|
30
|
+
{ k: "wechat", label: "微信" },
|
|
30
31
|
];
|
|
31
32
|
return names
|
|
32
33
|
.filter(({ k }) => {
|
|
@@ -76,19 +77,19 @@ function printManualInstructions(configPath) {
|
|
|
76
77
|
"appSecret": "你的飞书 App Secret(可选)",
|
|
77
78
|
"allowedUserIds": ["允许访问的飞书用户 ID(可选)"]
|
|
78
79
|
},
|
|
79
|
-
"wechat": {
|
|
80
|
-
"enabled": false,
|
|
81
|
-
"appId": "你的微信 App ID(可选)",
|
|
82
|
-
"appSecret": "你的微信 App Secret(可选)",
|
|
83
|
-
"wsUrl": "AGP WebSocket URL(可选,默认使用官方服务)",
|
|
84
|
-
"allowedUserIds": ["允许访问的微信用户 ID(可选)"]
|
|
85
|
-
},
|
|
86
80
|
"wework": {
|
|
87
81
|
"enabled": false,
|
|
88
82
|
"corpId": "你的企业微信 Corp ID(可选)",
|
|
89
83
|
"agentId": "你的企业微信 Agent ID(可选)",
|
|
90
84
|
"secret": "你的企业微信 Secret(可选)",
|
|
91
85
|
"allowedUserIds": ["允许访问的企业微信用户 ID(可选)"]
|
|
86
|
+
},
|
|
87
|
+
"wechat": {
|
|
88
|
+
"enabled": false,
|
|
89
|
+
"appId": "你的微信 App ID(可选,测试中)",
|
|
90
|
+
"appSecret": "你的微信 App Secret(可选)",
|
|
91
|
+
"wsUrl": "AGP WebSocket URL(可选,默认使用官方服务)",
|
|
92
|
+
"allowedUserIds": ["允许访问的微信用户 ID(可选)"]
|
|
92
93
|
}
|
|
93
94
|
},
|
|
94
95
|
"claudeWorkDir": "${process.cwd().replace(/\\/g, "/")}",
|
|
@@ -141,16 +142,16 @@ export async function runInteractiveSetup() {
|
|
|
141
142
|
(hasFs ? " ✓已配置" : ""),
|
|
142
143
|
value: "feishu",
|
|
143
144
|
},
|
|
144
|
-
{
|
|
145
|
-
title: "微信 (WeChat) - 扫码登录获取 token(QClaw/AGP 协议)" +
|
|
146
|
-
(hasWc ? " ✓已配置" : ""),
|
|
147
|
-
value: "wechat",
|
|
148
|
-
},
|
|
149
145
|
{
|
|
150
146
|
title: "企业微信 (WeCom/WeWork) - 需要 Bot ID 和 Secret" +
|
|
151
147
|
(hasWw ? " ✓已配置" : ""),
|
|
152
148
|
value: "wework",
|
|
153
149
|
},
|
|
150
|
+
{
|
|
151
|
+
title: "微信 (WeChat) - 扫码登录获取 token(QClaw/AGP 协议,测试中)" +
|
|
152
|
+
(hasWc ? " ✓已配置" : ""),
|
|
153
|
+
value: "wechat",
|
|
154
|
+
},
|
|
154
155
|
{ title: "配置多个平台", value: "multi" },
|
|
155
156
|
],
|
|
156
157
|
initial: 0,
|
|
@@ -170,8 +171,8 @@ export async function runInteractiveSetup() {
|
|
|
170
171
|
choices: [
|
|
171
172
|
{ title: "Telegram" + (hasTg ? " ✓已配置" : ""), value: "telegram", selected: hasTg },
|
|
172
173
|
{ title: "飞书 (Feishu)" + (hasFs ? " ✓已配置" : ""), value: "feishu", selected: hasFs },
|
|
173
|
-
{ title: "微信 (WeChat)" + (hasWc ? " ✓已配置" : ""), value: "wechat", selected: hasWc },
|
|
174
174
|
{ title: "企业微信 (WeWork)" + (hasWw ? " ✓已配置" : ""), value: "wework", selected: hasWw },
|
|
175
|
+
{ title: "微信 (WeChat,测试中)" + (hasWc ? " ✓已配置" : ""), value: "wechat", selected: hasWc },
|
|
175
176
|
],
|
|
176
177
|
}, { onCancel });
|
|
177
178
|
if (!multiResp.platforms || multiResp.platforms.length === 0) {
|
|
@@ -496,3 +497,59 @@ export async function runInteractiveSetup() {
|
|
|
496
497
|
console.log("");
|
|
497
498
|
return true;
|
|
498
499
|
}
|
|
500
|
+
const PLATFORM_LABELS = {
|
|
501
|
+
telegram: "Telegram",
|
|
502
|
+
feishu: "飞书",
|
|
503
|
+
wework: "企业微信",
|
|
504
|
+
wechat: "微信(测试中)",
|
|
505
|
+
};
|
|
506
|
+
/**
|
|
507
|
+
* 多通道启动时让用户确认/选择要启用的平台
|
|
508
|
+
* 仅当配置了 2 个及以上平台且 stdin 为 TTY 时调用
|
|
509
|
+
* @returns 更新后的 config,或 null 表示取消
|
|
510
|
+
*/
|
|
511
|
+
export async function runPlatformSelectionPrompt(config) {
|
|
512
|
+
const withCreds = getPlatformsWithCredentials(config);
|
|
513
|
+
if (withCreds.length <= 1)
|
|
514
|
+
return config;
|
|
515
|
+
const configPath = join(APP_HOME, "config.json");
|
|
516
|
+
const existing = loadExistingConfig();
|
|
517
|
+
const choices = withCreds.map((p) => ({
|
|
518
|
+
title: `${PLATFORM_LABELS[p]}${config.enabledPlatforms.includes(p) ? " ✓启用" : ""}`,
|
|
519
|
+
value: p,
|
|
520
|
+
selected: config.enabledPlatforms.includes(p),
|
|
521
|
+
}));
|
|
522
|
+
console.log("\n━━━ 选择要启用的平台 ━━━\n");
|
|
523
|
+
const resp = await prompts({
|
|
524
|
+
type: "multiselect",
|
|
525
|
+
name: "platforms",
|
|
526
|
+
message: "选择要启用的平台(空格切换,回车确认)",
|
|
527
|
+
choices,
|
|
528
|
+
min: 1,
|
|
529
|
+
hint: "至少选择 1 个平台",
|
|
530
|
+
}, {
|
|
531
|
+
onCancel: () => {
|
|
532
|
+
console.log("\n已取消启动。");
|
|
533
|
+
process.exit(0);
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
if (!resp.platforms || !Array.isArray(resp.platforms))
|
|
537
|
+
return null;
|
|
538
|
+
const selected = new Set(resp.platforms);
|
|
539
|
+
// 更新 config.json 中的 platforms.xxx.enabled,保留其他字段
|
|
540
|
+
const updated = { ...existing };
|
|
541
|
+
if (!updated.platforms)
|
|
542
|
+
updated.platforms = {};
|
|
543
|
+
for (const p of withCreds) {
|
|
544
|
+
const plat = updated.platforms[p] ?? {};
|
|
545
|
+
updated.platforms[p] = {
|
|
546
|
+
...plat,
|
|
547
|
+
enabled: selected.has(p),
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
const dir = dirname(configPath);
|
|
551
|
+
if (!existsSync(dir))
|
|
552
|
+
mkdirSync(dir, { recursive: true });
|
|
553
|
+
writeFileSync(configPath, JSON.stringify(updated, null, 2), "utf-8");
|
|
554
|
+
return loadConfig();
|
|
555
|
+
}
|