@wu529778790/open-im 1.9.1-beta.1 → 1.9.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
@@ -6,7 +6,7 @@ Multi-platform IM bridge for AI CLI tools. Connect Telegram, Feishu, WeCom, Ding
6
6
 
7
7
  ## Features
8
8
 
9
- - Multi-platform support: Telegram, Feishu, WeCom, DingTalk, QQ, WeChat (experimental), and WorkBuddy (WeChat KF via CodeBuddy), with multiple platforms enabled at the same time
9
+ - Multi-platform support: Telegram, Feishu, WeCom, DingTalk, QQ, and WeChat (WorkBuddy), with multiple platforms enabled at the same time
10
10
  - Multiple AI tools: Claude, Codex, and CodeBuddy
11
11
  - Per-platform AI routing: each IM platform can use a different AI tool, with `aiCommand` as the global default and `platforms.<name>.aiCommand` as the override
12
12
  - Streaming replies: relay AI output and tool execution progress in real time (DingTalk streaming is not fully supported yet)
@@ -56,7 +56,7 @@ Open the config page at [`http://127.0.0.1:39282`](http://127.0.0.1:39282) (or t
56
56
  - **AI Tooling** – **General**: default AI tool (Claude / Codex / CodeBuddy), work directory, hook port, log level. **Per-tool tabs**: Claude (CLI path, timeout, proxy, config path, ANTHROPIC\_\* fields), Codex (CLI path, timeout, proxy), CodeBuddy (CLI path, timeout).
57
57
  - **Service control** – Validate config, Save, Start bridge, Stop bridge.
58
58
 
59
- WeChat and WorkBuddy are not in the web UI; configure them in `~/.open-im/config.json` or via `open-im init`.
59
+ WorkBuddy (WeChat) is not in the web UI; configure it in `~/.open-im/config.json` or via `open-im init`.
60
60
 
61
61
  - `open-im start` serves both the config page and the bridge on your local machine.
62
62
  - `open-im dev` opens the page automatically only when setup is incomplete.
@@ -234,13 +234,6 @@ The following is valid JSON and can be saved directly as `~/.open-im/config.json
234
234
  "clientSecret": "YOUR_DINGTALK_CLIENT_SECRET",
235
235
  "cardTemplateId": "YOUR_DINGTALK_AI_CARD_TEMPLATE_ID"
236
236
  },
237
- "wechat": {
238
- "enabled": false,
239
- "aiCommand": "claude",
240
- "allowedUserIds": [],
241
- "appId": "YOUR_WECHAT_APP_ID",
242
- "appSecret": "YOUR_WECHAT_APP_SECRET"
243
- },
244
237
  "workbuddy": {
245
238
  "enabled": false,
246
239
  "aiCommand": "claude",
@@ -288,13 +281,6 @@ The following is valid JSON and can be saved directly as `~/.open-im/config.json
288
281
  | `WEWORK_SECRET` | WeCom secret |
289
282
  | `WEWORK_WS_URL` | WeCom WebSocket URL |
290
283
  | `WEWORK_ALLOWED_USER_IDS` | WeCom allowlist |
291
- | `WECHAT_APP_ID` | WeChat standard mode app ID |
292
- | `WECHAT_APP_SECRET` | WeChat standard mode app secret |
293
- | `WECHAT_TOKEN` | WeChat AGP mode token |
294
- | `WECHAT_GUID` | WeChat AGP mode GUID |
295
- | `WECHAT_USER_ID` | WeChat AGP mode user ID |
296
- | `WECHAT_WS_URL` | WeChat WebSocket URL |
297
- | `WECHAT_ALLOWED_USER_IDS` | WeChat allowlist |
298
284
  | `WORKBUDDY_ACCESS_TOKEN` | WorkBuddy OAuth access token (auto-generated by `open-im init`) |
299
285
  | `WORKBUDDY_REFRESH_TOKEN` | WorkBuddy OAuth refresh token (auto-generated by `open-im init`) |
300
286
  | `WORKBUDDY_USER_ID` | WorkBuddy user ID |
@@ -310,8 +296,7 @@ The following is valid JSON and can be saved directly as `~/.open-im/config.json
310
296
  - QQ: create a bot in the [QQ Open Platform](https://bot.q.qq.com/) and get the `App ID` and `App Secret`
311
297
  - DingTalk: create an internal enterprise app in DingTalk Open Platform, enable bot Stream Mode, and get the `Client ID` and `Client Secret`
312
298
  - WeCom: get the bot ID and secret from the [WeCom admin console](https://work.weixin.qq.com/)
313
- - WeChat: experimental, supports both standard mode and AGP/Qclaw-related settings
314
- - WorkBuddy: connects via CodeBuddy (copilot.tencent.com) Centrifuge WebSocket; run `open-im init` and select "WorkBuddy 微信客服 (WeChat KF)" to complete OAuth login and WeChat KF binding
299
+ - WeChat (WorkBuddy): connects via CodeBuddy (copilot.tencent.com) Centrifuge WebSocket; run `open-im init` and select "WorkBuddy 微信客服 (WeChat KF)" to complete OAuth login and WeChat KF binding
315
300
 
316
301
  Notes on DingTalk: the current implementation uses a hybrid model of "Stream Mode for receiving messages + OpenAPI for sending messages".
317
302
 
package/README.zh-CN.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## 功能特性
8
8
 
9
- - 多平台:支持 Telegram、飞书、企业微信、钉钉、QQ、微信(测试中)、WorkBuddy(通过 CodeBuddy 接入微信客服),可同时启用
9
+ - 多平台:支持 Telegram、飞书、企业微信、钉钉、QQ、微信(WorkBuddy),可同时启用
10
10
  - 多 AI 工具:支持 Claude、Codex、CodeBuddy
11
11
  - 按平台分配 AI:根级 `aiCommand` 作为默认值,`platforms.<name>.aiCommand` 可为不同 IM 单独指定 AI 工具
12
12
  - 流式输出:实时回传 AI 回复与工具执行进度(目前钉钉暂未实现流式传输)
@@ -62,7 +62,7 @@ open-im start
62
62
  - **AI 工具配置** – **公共**:默认 AI 工具(Claude / Codex / CodeBuddy)、工作目录、Hook 端口、日志级别。**分工具**:Claude(CLI 路径、超时、代理、配置路径、ANTHROPIC\_\* 等)、Codex(CLI 路径、超时、代理)、CodeBuddy(CLI 路径、超时)
63
63
  - **服务控制** – 校验配置、保存、启动桥接、停止桥接
64
64
 
65
- 微信和 WorkBuddy 暂不在网页中配置,如需使用请在 `~/.open-im/config.json` 中手动配置或通过 `open-im init` 引导。
65
+ WorkBuddy(微信)暂不在网页中配置,如需使用请在 `~/.open-im/config.json` 中手动配置或通过 `open-im init` 引导。
66
66
 
67
67
  - `open-im start` 会同时启动桥接服务并提供该配置页(本机场景)。
68
68
  - `open-im dev` 仅在未完成配置时自动打开页面。
@@ -240,13 +240,6 @@ codebuddy login
240
240
  "clientSecret": "YOUR_DINGTALK_CLIENT_SECRET",
241
241
  "cardTemplateId": "YOUR_DINGTALK_AI_CARD_TEMPLATE_ID"
242
242
  },
243
- "wechat": {
244
- "enabled": false,
245
- "aiCommand": "claude",
246
- "allowedUserIds": [],
247
- "appId": "YOUR_WECHAT_APP_ID",
248
- "appSecret": "YOUR_WECHAT_APP_SECRET"
249
- },
250
243
  "workbuddy": {
251
244
  "enabled": false,
252
245
  "aiCommand": "claude",
@@ -294,13 +287,6 @@ codebuddy login
294
287
  | `WEWORK_SECRET` | 企业微信 Secret |
295
288
  | `WEWORK_WS_URL` | 企业微信 WebSocket 地址 |
296
289
  | `WEWORK_ALLOWED_USER_IDS` | 企业微信白名单 |
297
- | `WECHAT_APP_ID` | 微信标准模式 App ID |
298
- | `WECHAT_APP_SECRET` | 微信标准模式 App Secret |
299
- | `WECHAT_TOKEN` | 微信 AGP 模式 Token |
300
- | `WECHAT_GUID` | 微信 AGP 模式 GUID |
301
- | `WECHAT_USER_ID` | 微信 AGP 模式 User ID |
302
- | `WECHAT_WS_URL` | 微信 WebSocket 地址 |
303
- | `WECHAT_ALLOWED_USER_IDS` | 微信白名单 |
304
290
  | `WORKBUDDY_ACCESS_TOKEN` | WorkBuddy OAuth 访问令牌(由 `open-im init` 自动生成) |
305
291
  | `WORKBUDDY_REFRESH_TOKEN` | WorkBuddy OAuth 刷新令牌(由 `open-im init` 自动生成) |
306
292
  | `WORKBUDDY_USER_ID` | WorkBuddy 用户 ID |
@@ -316,8 +302,7 @@ codebuddy login
316
302
  - QQ:从 [QQ 开放平台](https://bot.q.qq.com/) 创建机器人,获取 `App ID` 和 `App Secret`
317
303
  - 钉钉:从钉钉开放平台创建企业内部应用,启用机器人 Stream Mode,获取 `Client ID` 和 `Client Secret`
318
304
  - 企业微信:从 [企业微信管理后台](https://work.weixin.qq.com/) 获取 Bot ID 和 Secret
319
- - 微信:测试中,支持标准模式和 AGP/Qclaw 相关配置
320
- - WorkBuddy:通过 CodeBuddy(copilot.tencent.com)Centrifuge WebSocket 接入微信客服;运行 `open-im init` 并选择 "WorkBuddy 微信客服 (WeChat KF)" 完成 OAuth 登录和微信客服绑定
305
+ - 微信(WorkBuddy):通过 CodeBuddy(copilot.tencent.com)Centrifuge WebSocket 接入微信客服;运行 `open-im init` 并选择 "WorkBuddy 微信客服 (WeChat KF)" 完成 OAuth 登录和微信客服绑定
321
306
 
322
307
  说明:钉钉当前采用“Stream Mode 收消息 + OpenAPI 发送消息”的混合模式。
323
308
 
package/dist/config.d.ts CHANGED
@@ -188,8 +188,6 @@ export declare function getClaudeConfigHome(): string;
188
188
  export declare function loadClaudeSettingsEnv(): Record<string, string>;
189
189
  /** 保存环境变量到 Claude Code 配置文件(~/.claude/settings.json) */
190
190
  export declare function saveClaudeSettingsEnv(env: Record<string, string>): void;
191
- /** 检查是否已配置 Claude API 凭证 */
192
- export declare function hasClaudeCredentials(): boolean;
193
191
  /** 检测是否需要交互式配置(无 token 且无环境变量) */
194
192
  export declare function needsSetup(): boolean;
195
193
  export declare function loadConfig(): Config;
package/dist/config.js CHANGED
@@ -163,7 +163,7 @@ export function saveClaudeSettingsEnv(env) {
163
163
  }
164
164
  }
165
165
  /** 检查是否已配置 Claude API 凭证 */
166
- export function hasClaudeCredentials() {
166
+ function hasClaudeCredentials() {
167
167
  return !!(process.env.ANTHROPIC_API_KEY ||
168
168
  process.env.ANTHROPIC_AUTH_TOKEN ||
169
169
  process.env.CLAUDE_CODE_OAUTH_TOKEN ||
@@ -9,8 +9,6 @@ export declare const TERMINAL_ONLY_COMMANDS: Set<string>;
9
9
  export declare const CARDKIT_THROTTLE_MS = 80;
10
10
  /** Telegram 编辑消息节流:200ms(open-im 默认值) */
11
11
  export declare const TELEGRAM_THROTTLE_MS = 200;
12
- /** WeChat 流式更新节流:1000ms(AGP 协议建议值) */
13
- export declare const WECHAT_THROTTLE_MS = 1000;
14
12
  /** WorkBuddy 流式更新节流:1000ms(Centrifuge 协议建议值) */
15
13
  export declare const WORKBUDDY_THROTTLE_MS = 1000;
16
14
  export declare const WEWORK_THROTTLE_MS = 500;
package/dist/constants.js CHANGED
@@ -30,8 +30,6 @@ export const TERMINAL_ONLY_COMMANDS = new Set([
30
30
  export const CARDKIT_THROTTLE_MS = 80;
31
31
  /** Telegram 编辑消息节流:200ms(open-im 默认值) */
32
32
  export const TELEGRAM_THROTTLE_MS = 200;
33
- /** WeChat 流式更新节流:1000ms(AGP 协议建议值) */
34
- export const WECHAT_THROTTLE_MS = 1000;
35
33
  /** WorkBuddy 流式更新节流:1000ms(Centrifuge 协议建议值) */
36
34
  export const WORKBUDDY_THROTTLE_MS = 1000;
37
35
  export const WEWORK_THROTTLE_MS = 500;
@@ -12,8 +12,6 @@ interface CardOptions {
12
12
  toolName?: string;
13
13
  }
14
14
  export declare function truncateForStreaming(text: string): string;
15
- /** CardKit 2.0 格式,含 element_id 供 cardElement.content 流式更新 */
16
- export declare function buildCardV2Object(options: CardOptions, cardId?: string): Record<string, unknown>;
17
15
  export declare function buildCardV2(options: CardOptions, cardId?: string): string;
18
16
  export declare function splitLongContent(text: string, maxLen?: number): string[];
19
17
  export {};
@@ -27,7 +27,7 @@ export function truncateForStreaming(text) {
27
27
  return truncateText(text, MAX_STREAMING_CONTENT_LENGTH);
28
28
  }
29
29
  /** CardKit 2.0 格式,含 element_id 供 cardElement.content 流式更新 */
30
- export function buildCardV2Object(options, cardId) {
30
+ function buildCardV2Object(options, cardId) {
31
31
  const { content, status, note, thinking, toolName: rawToolName } = options;
32
32
  const toolName = getAIToolDisplayName(rawToolName ?? 'claude');
33
33
  const elements = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.9.1-beta.1",
3
+ "version": "1.9.1",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, CodeBuddy)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,17 +0,0 @@
1
- import type { StreamEvent } from './types.js';
2
- export declare function parseStreamLine(line: string): StreamEvent | null;
3
- export declare function extractTextDelta(event: StreamEvent): {
4
- text: string;
5
- } | null;
6
- export declare function extractThinkingDelta(event: StreamEvent): {
7
- text: string;
8
- } | null;
9
- export declare function extractResult(event: StreamEvent): {
10
- success: boolean;
11
- result: string;
12
- accumulated: string;
13
- cost: number;
14
- durationMs: number;
15
- numTurns: number;
16
- toolStats: Record<string, number>;
17
- } | null;
@@ -1,50 +0,0 @@
1
- export function parseStreamLine(line) {
2
- const trimmed = line.trim();
3
- if (!trimmed)
4
- return null;
5
- try {
6
- const parsed = JSON.parse(trimmed);
7
- if (typeof parsed === 'object' && parsed !== null && 'type' in parsed) {
8
- return parsed;
9
- }
10
- }
11
- catch {
12
- /* ignore */
13
- }
14
- return null;
15
- }
16
- export function extractTextDelta(event) {
17
- const e = event;
18
- if (e.type === 'stream_event' &&
19
- e.event?.type === 'content_block_delta' &&
20
- e.event.delta?.type === 'text_delta' &&
21
- e.event.delta.text) {
22
- return { text: e.event.delta.text };
23
- }
24
- return null;
25
- }
26
- export function extractThinkingDelta(event) {
27
- const e = event;
28
- if (e.type === 'stream_event' &&
29
- e.event?.type === 'content_block_delta' &&
30
- e.event.delta?.type === 'thinking_delta' &&
31
- e.event.delta.thinking) {
32
- return { text: e.event.delta.thinking };
33
- }
34
- return null;
35
- }
36
- export function extractResult(event) {
37
- if (event.type === 'result' && 'subtype' in event) {
38
- const e = event;
39
- return {
40
- success: e.subtype === 'success',
41
- result: e.result,
42
- accumulated: '',
43
- cost: e.total_cost_usd ?? 0,
44
- durationMs: e.duration_ms ?? 0,
45
- numTurns: e.num_turns ?? 0,
46
- toolStats: {},
47
- };
48
- }
49
- return null;
50
- }
@@ -1,54 +0,0 @@
1
- export interface StreamInit {
2
- type: 'system';
3
- subtype: 'init';
4
- session_id: string;
5
- model: string;
6
- }
7
- export interface StreamContentBlockDelta {
8
- type: 'stream_event';
9
- event: {
10
- type: 'content_block_delta';
11
- index: number;
12
- delta: {
13
- type: string;
14
- text?: string;
15
- thinking?: string;
16
- partial_json?: string;
17
- };
18
- };
19
- }
20
- export interface StreamContentBlockStop {
21
- type: 'stream_event';
22
- event: {
23
- type: 'content_block_stop';
24
- index: number;
25
- };
26
- }
27
- export interface StreamContentBlockStart {
28
- type: 'stream_event';
29
- event: {
30
- type: 'content_block_start';
31
- index: number;
32
- content_block: {
33
- type: string;
34
- name?: string;
35
- };
36
- };
37
- }
38
- export interface StreamResult {
39
- type: 'result';
40
- subtype: 'success' | 'error';
41
- result: string;
42
- total_cost_usd: number;
43
- duration_ms: number;
44
- num_turns: number;
45
- }
46
- export type StreamEvent = StreamInit | StreamContentBlockDelta | StreamContentBlockStart | StreamContentBlockStop | StreamResult | {
47
- type: string;
48
- [key: string]: unknown;
49
- };
50
- export declare function isStreamInit(e: StreamEvent): e is StreamInit;
51
- export declare function isContentBlockDelta(e: StreamEvent): e is StreamContentBlockDelta;
52
- export declare function isContentBlockStart(e: StreamEvent): e is StreamContentBlockStart;
53
- export declare function isContentBlockStop(e: StreamEvent): e is StreamContentBlockStop;
54
- export declare function isStreamResult(e: StreamEvent): e is StreamResult;
@@ -1,21 +0,0 @@
1
- export function isStreamInit(e) {
2
- return e.type === 'system' && 'subtype' in e && e.subtype === 'init';
3
- }
4
- export function isContentBlockDelta(e) {
5
- return (e.type === 'stream_event' &&
6
- typeof e.event === 'object' &&
7
- e.event.type === 'content_block_delta');
8
- }
9
- export function isContentBlockStart(e) {
10
- return (e.type === 'stream_event' &&
11
- typeof e.event === 'object' &&
12
- e.event.type === 'content_block_start');
13
- }
14
- export function isContentBlockStop(e) {
15
- return (e.type === 'stream_event' &&
16
- typeof e.event === 'object' &&
17
- e.event.type === 'content_block_stop');
18
- }
19
- export function isStreamResult(e) {
20
- return e.type === 'result' && 'subtype' in e;
21
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * WorkBuddy Module - CodeBuddy OAuth + Centrifuge WebSocket for WeChat
3
- */
4
- export * from './types.js';
5
- export * from './oauth.js';
6
- export * from './centrifuge-client.js';
7
- export * from './message-sender.js';
8
- export * from './client.js';
@@ -1,8 +0,0 @@
1
- /**
2
- * WorkBuddy Module - CodeBuddy OAuth + Centrifuge WebSocket for WeChat
3
- */
4
- export * from './types.js';
5
- export * from './oauth.js';
6
- export * from './centrifuge-client.js';
7
- export * from './message-sender.js';
8
- export * from './client.js';