@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 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
- 微信使用 AGP 协议连接,该协议服务器由第三方提供。如果出现连接失败,可能原因:
293
+ 微信通道基于 **Qclaw** 接入,目前处于**测试阶段**,连接可能不稳定。若出现连接失败,可能原因:
294
294
 
295
- 1. **AGP 服务器限制**
296
- - AGP 服务器使用了 Qclaw 通道,可能有白名单限制
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
- **注意**:这是 AGP 协议服务器的限制,非本项目的 bug。如果 AGP 服务器调整策略,微信功能可能恢复正常。
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
- const config = loadConfig();
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.1.0",
3
+ "version": "1.1.1-beta.1",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, Cursor)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",