@wu529778790/open-im 1.10.9-beta.2 → 1.10.9-beta.21
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 +43 -62
- package/README.zh-CN.md +43 -62
- package/dist/adapters/claude-sdk-adapter.d.ts +13 -0
- package/dist/adapters/claude-sdk-adapter.js +221 -23
- package/dist/adapters/registry.js +3 -0
- package/dist/channels/capabilities.js +5 -0
- package/dist/clawbot/client.d.ts +14 -0
- package/dist/clawbot/client.js +299 -0
- package/dist/clawbot/event-handler.d.ts +12 -0
- package/dist/clawbot/event-handler.js +85 -0
- package/dist/clawbot/message-sender.d.ts +18 -0
- package/dist/clawbot/message-sender.js +109 -0
- package/dist/clawbot/qr-login.d.ts +33 -0
- package/dist/clawbot/qr-login.js +120 -0
- package/dist/clawbot/types.d.ts +111 -0
- package/dist/clawbot/types.js +7 -0
- package/dist/codebuddy/cli-runner.js +31 -2
- package/dist/codex/cli-runner.js +28 -2
- package/dist/config/file-io.d.ts +6 -3
- package/dist/config/file-io.js +12 -7
- package/dist/config/types.d.ts +17 -1
- package/dist/config-web-page-i18n.d.ts +24 -2
- package/dist/config-web-page-i18n.js +24 -2
- package/dist/config-web.js +79 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +38 -2
- package/dist/constants.d.ts +6 -0
- package/dist/constants.js +6 -0
- package/dist/dingtalk/client.js +2 -1
- package/dist/dingtalk/event-handler.js +1 -1
- package/dist/index.js +50 -0
- package/dist/qq/client.js +7 -1
- package/dist/queue/request-queue.js +11 -10
- package/dist/setup.js +131 -3
- package/dist/shared/active-chats.d.ts +2 -2
- package/dist/shared/ai-task.d.ts +14 -0
- package/dist/shared/ai-task.js +57 -9
- package/dist/shared/process-kill.d.ts +24 -0
- package/dist/shared/process-kill.js +79 -0
- package/dist/shared/reconnect.d.ts +28 -0
- package/dist/shared/reconnect.js +56 -0
- package/dist/shared/task-cleanup.d.ts +16 -0
- package/dist/shared/task-cleanup.js +34 -1
- package/dist/telegram/client.js +7 -1
- package/dist/telemetry/telemetry-upload.js +1 -1
- package/dist/wework/client.js +17 -5
- package/dist/wework/event-handler.js +3 -0
- package/dist/workbuddy/centrifuge-client.d.ts +4 -0
- package/dist/workbuddy/centrifuge-client.js +76 -28
- package/dist/workbuddy/client.js +39 -2
- package/package.json +1 -1
- package/web/dist/assets/index-BaLTMeeF.js +57 -0
- package/web/dist/index.html +1 -1
- package/dist/config/credentials.d.ts +0 -19
- package/dist/config/credentials.js +0 -36
- package/web/dist/assets/index-B-oVSMUp.js +0 -57
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawBot Types - WeChat iLink Bot API
|
|
3
|
+
*
|
|
4
|
+
* Matches the official iLink API protocol (POST + JSON body + Bearer token).
|
|
5
|
+
* Reference: @tencent-weixin/openclaw-weixin, cc-wechat, claude-code-wechat-channel
|
|
6
|
+
*/
|
|
7
|
+
/** Connection state */
|
|
8
|
+
export type ClawBotState = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
9
|
+
/** ClawBot configuration */
|
|
10
|
+
export interface ClawBotConfig {
|
|
11
|
+
/** iLink API base URL (default: https://ilinkai.weixin.qq.com) */
|
|
12
|
+
apiUrl: string;
|
|
13
|
+
/** Bearer token for authentication */
|
|
14
|
+
apiToken: string;
|
|
15
|
+
}
|
|
16
|
+
/** iLink message content item types */
|
|
17
|
+
export declare const enum MessageItemType {
|
|
18
|
+
NONE = 0,
|
|
19
|
+
TEXT = 1,
|
|
20
|
+
IMAGE = 2,
|
|
21
|
+
VOICE = 3,
|
|
22
|
+
FILE = 4,
|
|
23
|
+
VIDEO = 5
|
|
24
|
+
}
|
|
25
|
+
/** Text content item */
|
|
26
|
+
export interface TextItem {
|
|
27
|
+
text?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Image content item */
|
|
30
|
+
export interface ImageItem {
|
|
31
|
+
media?: {
|
|
32
|
+
aes_key?: string;
|
|
33
|
+
cdn_url?: string;
|
|
34
|
+
};
|
|
35
|
+
width?: number;
|
|
36
|
+
height?: number;
|
|
37
|
+
}
|
|
38
|
+
/** Voice content item */
|
|
39
|
+
export interface VoiceItem {
|
|
40
|
+
text?: string;
|
|
41
|
+
media?: {
|
|
42
|
+
aes_key?: string;
|
|
43
|
+
cdn_url?: string;
|
|
44
|
+
};
|
|
45
|
+
playtime?: number;
|
|
46
|
+
}
|
|
47
|
+
/** File content item */
|
|
48
|
+
export interface FileItem {
|
|
49
|
+
file_name?: string;
|
|
50
|
+
file_size?: number;
|
|
51
|
+
media?: {
|
|
52
|
+
aes_key?: string;
|
|
53
|
+
cdn_url?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Video content item */
|
|
57
|
+
export interface VideoItem {
|
|
58
|
+
media?: {
|
|
59
|
+
aes_key?: string;
|
|
60
|
+
cdn_url?: string;
|
|
61
|
+
};
|
|
62
|
+
duration_ms?: number;
|
|
63
|
+
}
|
|
64
|
+
/** A single content item in a message */
|
|
65
|
+
export interface MessageItem {
|
|
66
|
+
type?: number;
|
|
67
|
+
text_item?: TextItem;
|
|
68
|
+
image_item?: ImageItem;
|
|
69
|
+
voice_item?: VoiceItem;
|
|
70
|
+
file_item?: FileItem;
|
|
71
|
+
video_item?: VideoItem;
|
|
72
|
+
ref_msg?: {
|
|
73
|
+
title?: string;
|
|
74
|
+
message_item?: MessageItem;
|
|
75
|
+
};
|
|
76
|
+
msg_id?: string;
|
|
77
|
+
}
|
|
78
|
+
/** iLink message from getupdates */
|
|
79
|
+
export interface ILinkMessage {
|
|
80
|
+
seq?: number;
|
|
81
|
+
message_id?: number;
|
|
82
|
+
from_user_id?: string;
|
|
83
|
+
to_user_id?: string;
|
|
84
|
+
client_id?: string;
|
|
85
|
+
create_time_ms?: number;
|
|
86
|
+
session_id?: string;
|
|
87
|
+
/** 1=USER (inbound), 2=BOT (outbound) */
|
|
88
|
+
message_type?: number;
|
|
89
|
+
/** 0=NEW, 1=GENERATING, 2=FINISH */
|
|
90
|
+
message_state?: number;
|
|
91
|
+
item_list?: MessageItem[];
|
|
92
|
+
/** Token required for sending replies to this conversation */
|
|
93
|
+
context_token?: string;
|
|
94
|
+
group_id?: string;
|
|
95
|
+
}
|
|
96
|
+
/** getupdates response */
|
|
97
|
+
export interface GetUpdatesResponse {
|
|
98
|
+
ret?: number;
|
|
99
|
+
errcode?: number;
|
|
100
|
+
errmsg?: string;
|
|
101
|
+
msgs?: ILinkMessage[];
|
|
102
|
+
/** Opaque cursor for next poll — pass back as-is */
|
|
103
|
+
get_updates_buf?: string;
|
|
104
|
+
longpolling_timeout_ms?: number;
|
|
105
|
+
}
|
|
106
|
+
/** sendmessage response */
|
|
107
|
+
export interface SendMessageResponse {
|
|
108
|
+
ret?: number;
|
|
109
|
+
errcode?: number;
|
|
110
|
+
errmsg?: string;
|
|
111
|
+
}
|
|
@@ -3,6 +3,7 @@ import { accessSync, constants } from 'node:fs';
|
|
|
3
3
|
import { isAbsolute, join } from 'node:path';
|
|
4
4
|
import { createLogger } from '../logger.js';
|
|
5
5
|
import { processEnvForNonClaudeCliChild } from '../config/file-io.js';
|
|
6
|
+
import { killProcessTree, trackChild } from '../shared/process-kill.js';
|
|
6
7
|
const log = createLogger('CodeBuddyCli');
|
|
7
8
|
export function buildCodeBuddyArgs(prompt, sessionId, options) {
|
|
8
9
|
const args = ['--print', '--output-format', 'stream-json'];
|
|
@@ -200,10 +201,14 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
200
201
|
log.info(`Spawning CodeBuddy CLI: path=${normalizedCliPath}, cwd=${workDir}, session=${sessionId ?? 'new'}, args=${args.join(' ')}`);
|
|
201
202
|
const child = spawn(spawnCmd, spawnArgs, {
|
|
202
203
|
cwd: workDir,
|
|
204
|
+
// Unix 上放入独立进程组,使 abort/关停能用 process.kill(-pid) 杀掉整棵子树(含 MCP/shell 孙进程)
|
|
205
|
+
detached: process.platform !== 'win32',
|
|
203
206
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
204
207
|
env,
|
|
205
208
|
windowsHide: process.platform === 'win32',
|
|
206
209
|
});
|
|
210
|
+
trackChild(child);
|
|
211
|
+
let cliTimeoutHandle = null;
|
|
207
212
|
let accumulated = '';
|
|
208
213
|
let accumulatedThinking = '';
|
|
209
214
|
let completed = false;
|
|
@@ -311,6 +316,10 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
311
316
|
}
|
|
312
317
|
});
|
|
313
318
|
child.on('close', (code) => {
|
|
319
|
+
if (cliTimeoutHandle) {
|
|
320
|
+
clearTimeout(cliTimeoutHandle);
|
|
321
|
+
cliTimeoutHandle = null;
|
|
322
|
+
}
|
|
314
323
|
if (completed)
|
|
315
324
|
return;
|
|
316
325
|
if (stdoutState.buffer.trim()) {
|
|
@@ -343,16 +352,36 @@ export function runCodeBuddy(cliPath, prompt, sessionId, workDir, callbacks, opt
|
|
|
343
352
|
});
|
|
344
353
|
});
|
|
345
354
|
child.on('error', (err) => {
|
|
355
|
+
if (cliTimeoutHandle) {
|
|
356
|
+
clearTimeout(cliTimeoutHandle);
|
|
357
|
+
cliTimeoutHandle = null;
|
|
358
|
+
}
|
|
346
359
|
if (completed)
|
|
347
360
|
return;
|
|
348
361
|
completed = true;
|
|
349
362
|
callbacks.onError(`Failed to start CodeBuddy CLI: ${err.message}`);
|
|
350
363
|
});
|
|
364
|
+
// 墙钟超时:防止 CLI 挂死永久占用用户队列槽
|
|
365
|
+
const cliTimeoutMs = Number(process.env.OPEN_IM_CLI_TIMEOUT_MS) || 30 * 60 * 1000;
|
|
366
|
+
cliTimeoutHandle = setTimeout(() => {
|
|
367
|
+
if (completed)
|
|
368
|
+
return;
|
|
369
|
+
log.warn(`CodeBuddy CLI 超过 ${cliTimeoutMs}ms,强制终止 (pid=${child.pid})`);
|
|
370
|
+
completed = true;
|
|
371
|
+
killProcessTree(child);
|
|
372
|
+
callbacks.onError(`CodeBuddy CLI 运行超时(${Math.round(cliTimeoutMs / 1000)}s),已终止。请重试。`);
|
|
373
|
+
}, cliTimeoutMs);
|
|
374
|
+
cliTimeoutHandle.unref();
|
|
351
375
|
return {
|
|
352
376
|
abort: () => {
|
|
377
|
+
if (completed)
|
|
378
|
+
return;
|
|
353
379
|
completed = true;
|
|
354
|
-
if (
|
|
355
|
-
|
|
380
|
+
if (cliTimeoutHandle) {
|
|
381
|
+
clearTimeout(cliTimeoutHandle);
|
|
382
|
+
cliTimeoutHandle = null;
|
|
383
|
+
}
|
|
384
|
+
killProcessTree(child);
|
|
356
385
|
},
|
|
357
386
|
};
|
|
358
387
|
}
|
package/dist/codex/cli-runner.js
CHANGED
|
@@ -7,6 +7,7 @@ import { dirname, join } from 'node:path';
|
|
|
7
7
|
import { createInterface } from 'node:readline';
|
|
8
8
|
import { createLogger } from '../logger.js';
|
|
9
9
|
import { processEnvForNonClaudeCliChild } from '../config/file-io.js';
|
|
10
|
+
import { killProcessTree, trackChild } from '../shared/process-kill.js';
|
|
10
11
|
const log = createLogger('CodexCli');
|
|
11
12
|
const windowsCodexLaunchCache = new Map();
|
|
12
13
|
const SUPPORTED_IMAGE_EXTENSIONS = new Set([
|
|
@@ -201,10 +202,13 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
201
202
|
: args;
|
|
202
203
|
const child = spawn(spawnCmd, spawnArgs, {
|
|
203
204
|
cwd: workDir,
|
|
205
|
+
// Unix 上放入独立进程组,使 abort/关停能用 process.kill(-pid) 杀掉整棵子树(含 MCP/shell 孙进程)
|
|
206
|
+
detached: process.platform !== 'win32',
|
|
204
207
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
205
208
|
env,
|
|
206
209
|
windowsHide: process.platform === 'win32',
|
|
207
210
|
});
|
|
211
|
+
trackChild(child);
|
|
208
212
|
child.stdin?.write(prompt);
|
|
209
213
|
child.stdin?.end();
|
|
210
214
|
let accumulated = '';
|
|
@@ -327,7 +331,12 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
327
331
|
let exitCode = null;
|
|
328
332
|
let rlClosed = false;
|
|
329
333
|
let childClosed = false;
|
|
334
|
+
let cliTimeoutHandle = null;
|
|
330
335
|
const finalize = () => {
|
|
336
|
+
if (cliTimeoutHandle) {
|
|
337
|
+
clearTimeout(cliTimeoutHandle);
|
|
338
|
+
cliTimeoutHandle = null;
|
|
339
|
+
}
|
|
331
340
|
if (!rlClosed || !childClosed)
|
|
332
341
|
return;
|
|
333
342
|
if (completed)
|
|
@@ -387,12 +396,29 @@ export function runCodex(cliPath, prompt, sessionId, workDir, callbacks, options
|
|
|
387
396
|
childClosed = true;
|
|
388
397
|
finalize();
|
|
389
398
|
});
|
|
399
|
+
// 墙钟超时:防止 CLI 挂死(网络卡住、工具死循环等)永久占用用户队列槽
|
|
400
|
+
const cliTimeoutMs = Number(process.env.OPEN_IM_CLI_TIMEOUT_MS) || 30 * 60 * 1000;
|
|
401
|
+
cliTimeoutHandle = setTimeout(() => {
|
|
402
|
+
if (completed)
|
|
403
|
+
return;
|
|
404
|
+
log.warn(`Codex CLI 超过 ${cliTimeoutMs}ms,强制终止 (pid=${child.pid})`);
|
|
405
|
+
completed = true;
|
|
406
|
+
rl.close();
|
|
407
|
+
killProcessTree(child);
|
|
408
|
+
callbacks.onError(`Codex CLI 运行超时(${Math.round(cliTimeoutMs / 1000)}s),已终止。请重试。`);
|
|
409
|
+
}, cliTimeoutMs);
|
|
410
|
+
cliTimeoutHandle.unref();
|
|
390
411
|
return {
|
|
391
412
|
abort: () => {
|
|
413
|
+
if (completed)
|
|
414
|
+
return;
|
|
392
415
|
completed = true;
|
|
416
|
+
if (cliTimeoutHandle) {
|
|
417
|
+
clearTimeout(cliTimeoutHandle);
|
|
418
|
+
cliTimeoutHandle = null;
|
|
419
|
+
}
|
|
393
420
|
rl.close();
|
|
394
|
-
|
|
395
|
-
child.kill('SIGTERM');
|
|
421
|
+
killProcessTree(child);
|
|
396
422
|
},
|
|
397
423
|
};
|
|
398
424
|
}
|
package/dist/config/file-io.d.ts
CHANGED
|
@@ -19,8 +19,11 @@ export declare function getClaudeSdkRuntimeIssue(): string | null;
|
|
|
19
19
|
export declare function parseCommaSeparated(value: string): string[];
|
|
20
20
|
/**
|
|
21
21
|
* 将最新的 Claude 认证环境变量按优先级合并到 process.env。
|
|
22
|
-
* 优先级:shell 环境变量 >
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* 优先级:shell 环境变量 > 本机 Claude 配置(~/.claude/settings.json,与 Claude Code 共用)>
|
|
23
|
+
* ~/.open-im/config.json 的 tools.claude.env。
|
|
24
|
+
*
|
|
25
|
+
* 设计意图:用户只需维护 ~/.claude/settings.json(与 Claude Code CLI 共用),
|
|
26
|
+
* open-im 自动跟随本地 Claude 配置,无需单独配置。config.json 的 tools.claude.env
|
|
27
|
+
* 仅作为兜底,供没有本地 Claude 安装的场景使用。
|
|
25
28
|
*/
|
|
26
29
|
export declare function refreshClaudeEnvToProcess(): void;
|
package/dist/config/file-io.js
CHANGED
|
@@ -350,9 +350,12 @@ export function parseCommaSeparated(value) {
|
|
|
350
350
|
}
|
|
351
351
|
/**
|
|
352
352
|
* 将最新的 Claude 认证环境变量按优先级合并到 process.env。
|
|
353
|
-
* 优先级:shell 环境变量 >
|
|
354
|
-
*
|
|
355
|
-
*
|
|
353
|
+
* 优先级:shell 环境变量 > 本机 Claude 配置(~/.claude/settings.json,与 Claude Code 共用)>
|
|
354
|
+
* ~/.open-im/config.json 的 tools.claude.env。
|
|
355
|
+
*
|
|
356
|
+
* 设计意图:用户只需维护 ~/.claude/settings.json(与 Claude Code CLI 共用),
|
|
357
|
+
* open-im 自动跟随本地 Claude 配置,无需单独配置。config.json 的 tools.claude.env
|
|
358
|
+
* 仅作为兜底,供没有本地 Claude 安装的场景使用。
|
|
356
359
|
*/
|
|
357
360
|
export function refreshClaudeEnvToProcess() {
|
|
358
361
|
const file = loadFileConfig();
|
|
@@ -363,14 +366,16 @@ export function refreshClaudeEnvToProcess() {
|
|
|
363
366
|
process.env[key] = originalShellEnv[key];
|
|
364
367
|
continue;
|
|
365
368
|
}
|
|
366
|
-
|
|
367
|
-
process.env[key] = claudeToolEnv[key];
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
369
|
+
// 优先读取 ~/.claude/settings.json(与 Claude Code CLI 共用同一配置)
|
|
370
370
|
if (key in claudeSettingsEnv) {
|
|
371
371
|
process.env[key] = claudeSettingsEnv[key];
|
|
372
372
|
continue;
|
|
373
373
|
}
|
|
374
|
+
// 兜底:config.json tools.claude.env(仅在没有本地 Claude 安装时需要)
|
|
375
|
+
if (key in claudeToolEnv) {
|
|
376
|
+
process.env[key] = claudeToolEnv[key];
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
374
379
|
delete process.env[key];
|
|
375
380
|
}
|
|
376
381
|
}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LogLevel } from '../logger.js';
|
|
2
|
-
export type Platform = 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wework' | 'workbuddy';
|
|
2
|
+
export type Platform = 'clawbot' | 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wework' | 'workbuddy';
|
|
3
3
|
export type AiCommand = 'claude' | 'codex' | 'codebuddy';
|
|
4
4
|
export interface Config {
|
|
5
5
|
enabledPlatforms: Platform[];
|
|
@@ -21,6 +21,7 @@ export interface Config {
|
|
|
21
21
|
weworkAllowedUserIds: string[];
|
|
22
22
|
dingtalkAllowedUserIds: string[];
|
|
23
23
|
workbuddyAllowedUserIds: string[];
|
|
24
|
+
clawbotAllowedUserIds: string[];
|
|
24
25
|
codexCliPath: string;
|
|
25
26
|
codebuddyCliPath: string;
|
|
26
27
|
/** Claude 访问 API 的代理(如 http://127.0.0.1:7890) */
|
|
@@ -87,6 +88,13 @@ export interface Config {
|
|
|
87
88
|
guid?: string;
|
|
88
89
|
workspacePath?: string;
|
|
89
90
|
};
|
|
91
|
+
clawbot?: {
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
aiCommand?: AiCommand;
|
|
94
|
+
allowedUserIds: string[];
|
|
95
|
+
apiUrl?: string;
|
|
96
|
+
apiToken?: string;
|
|
97
|
+
};
|
|
90
98
|
};
|
|
91
99
|
}
|
|
92
100
|
export interface FilePlatformTelegram {
|
|
@@ -147,6 +155,13 @@ export interface FilePlatformWorkBuddy {
|
|
|
147
155
|
guid?: string;
|
|
148
156
|
workspacePath?: string;
|
|
149
157
|
}
|
|
158
|
+
export interface FilePlatformClawbot {
|
|
159
|
+
enabled?: boolean;
|
|
160
|
+
aiCommand?: AiCommand;
|
|
161
|
+
allowedUserIds?: string[];
|
|
162
|
+
apiUrl?: string;
|
|
163
|
+
apiToken?: string;
|
|
164
|
+
}
|
|
150
165
|
export interface FileToolClaude {
|
|
151
166
|
cliPath?: string;
|
|
152
167
|
workDir?: string;
|
|
@@ -180,6 +195,7 @@ export interface FileConfig {
|
|
|
180
195
|
wework?: FilePlatformWework;
|
|
181
196
|
dingtalk?: FilePlatformDingtalk;
|
|
182
197
|
workbuddy?: FilePlatformWorkBuddy;
|
|
198
|
+
clawbot?: FilePlatformClawbot;
|
|
183
199
|
};
|
|
184
200
|
/** @deprecated 仅旧配置兼容;运行时以各 platforms.*.aiCommand 为准 */
|
|
185
201
|
aiCommand?: string;
|
|
@@ -4,7 +4,7 @@ export declare const PAGE_TEXTS: {
|
|
|
4
4
|
readonly heroBadge: "open-im local control";
|
|
5
5
|
readonly heroTitle: "Local bridge control.";
|
|
6
6
|
readonly heroBody: "";
|
|
7
|
-
readonly heroBodyFull: "One local bridge for Telegram, Feishu, QQ, WeWork, DingTalk, and
|
|
7
|
+
readonly heroBodyFull: "One local bridge for Telegram, Feishu, QQ, WeWork, DingTalk, WorkBuddy, and ClawBot.";
|
|
8
8
|
readonly heroKicker: "Local AI bridge";
|
|
9
9
|
readonly langButton: "中文";
|
|
10
10
|
readonly darkModeToggle: "Toggle dark mode";
|
|
@@ -56,6 +56,16 @@ export declare const PAGE_TEXTS: {
|
|
|
56
56
|
readonly weworkSummary: "Corp ID and secret for enterprise delivery.";
|
|
57
57
|
readonly dingtalkSummary: "Client credentials plus optional card template.";
|
|
58
58
|
readonly workbuddySummary: "CodeBuddy OAuth for WeChat customer service.";
|
|
59
|
+
readonly clawbotSummary: "WeChat iLink API via ClawBot.";
|
|
60
|
+
readonly clawbotApiUrl: "API URL";
|
|
61
|
+
readonly clawbotApiToken: "API Token (Bearer)";
|
|
62
|
+
readonly clawbotHelp: "Click \"Scan QR Login\" to authenticate via WeChat, or paste token manually.";
|
|
63
|
+
readonly qrLoginBtn: "Scan QR Login";
|
|
64
|
+
readonly qrLoginScanning: "Waiting for QR code scan...";
|
|
65
|
+
readonly qrLoginSuccess: "Login successful! Credentials saved.";
|
|
66
|
+
readonly qrLoginFailed: "Login failed";
|
|
67
|
+
readonly qrLoginExpired: "QR code expired, please try again";
|
|
68
|
+
readonly qrScanHint: "Use WeChat to scan the QR code";
|
|
59
69
|
readonly platformCredentialsTitle: "Credentials";
|
|
60
70
|
readonly platformAccessTitle: "Routing and access";
|
|
61
71
|
readonly platformTestNote: "Checks required credentials against the platform.";
|
|
@@ -152,13 +162,14 @@ export declare const PAGE_TEXTS: {
|
|
|
152
162
|
readonly tipWeworkCorp: "WeCom admin → app → view Corp ID (or smart-bot Bot ID) and Secret.";
|
|
153
163
|
readonly tipDingtalkClient: "<a href=\"https://open-dev.dingtalk.com\" target=\"_blank\" rel=\"noopener\">DingTalk Open Platform</a> → internal app → App Key & App Secret.";
|
|
154
164
|
readonly tipWorkbuddyToken: "Use terminal <code>open-im init</code> for WorkBuddy OAuth, or paste tokens from CodeBuddy login.";
|
|
165
|
+
readonly tipClawbotApiToken: "Run <code>open-im init</code> and select ClawBot to scan QR code, or paste token from ClawBot iLink login.";
|
|
155
166
|
};
|
|
156
167
|
readonly zh: {
|
|
157
168
|
readonly pageTitle: "open-im 本地控制台";
|
|
158
169
|
readonly heroBadge: "open-im";
|
|
159
170
|
readonly heroTitle: "本地桥接控制台";
|
|
160
171
|
readonly heroBody: "";
|
|
161
|
-
readonly heroBodyFull: "一个本地桥接入口,统一连接 Telegram、Feishu、QQ、WeWork、DingTalk 和
|
|
172
|
+
readonly heroBodyFull: "一个本地桥接入口,统一连接 Telegram、Feishu、QQ、WeWork、DingTalk、WorkBuddy 和 ClawBot。";
|
|
162
173
|
readonly heroKicker: "本地 AI 桥接";
|
|
163
174
|
readonly langButton: "EN";
|
|
164
175
|
readonly darkModeToggle: "切换暗黑模式";
|
|
@@ -210,6 +221,16 @@ export declare const PAGE_TEXTS: {
|
|
|
210
221
|
readonly weworkSummary: "企业微信 Corp ID 与 Secret。";
|
|
211
222
|
readonly dingtalkSummary: "钉钉 Client 凭证,可选配置卡片模板 ID。";
|
|
212
223
|
readonly workbuddySummary: "CodeBuddy OAuth 连接微信客服。";
|
|
224
|
+
readonly clawbotSummary: "通过 ClawBot 连接微信 iLink API。";
|
|
225
|
+
readonly clawbotApiUrl: "API 地址";
|
|
226
|
+
readonly clawbotApiToken: "API Token (Bearer)";
|
|
227
|
+
readonly clawbotHelp: "点击“扫码登录”通过微信认证,或手动粘贴 Token。";
|
|
228
|
+
readonly qrLoginBtn: "扫码登录";
|
|
229
|
+
readonly qrLoginScanning: "等待扫码...";
|
|
230
|
+
readonly qrLoginSuccess: "登录成功!凭证已保存。";
|
|
231
|
+
readonly qrLoginFailed: "登录失败";
|
|
232
|
+
readonly qrLoginExpired: "二维码已过期,请重试";
|
|
233
|
+
readonly qrScanHint: "请使用微信扫描二维码";
|
|
213
234
|
readonly platformCredentialsTitle: "凭证";
|
|
214
235
|
readonly platformAccessTitle: "路由与访问";
|
|
215
236
|
readonly platformTestNote: "只会检查该平台的必填凭证是否可用。";
|
|
@@ -303,5 +324,6 @@ export declare const PAGE_TEXTS: {
|
|
|
303
324
|
readonly tipWeworkCorp: "企业微信管理后台 → 应用 → 查省 Corp ID / 智能机器人 Bot ID 与 Secret。";
|
|
304
325
|
readonly tipDingtalkClient: "<a href=\"https://open-dev.dingtalk.com\" target=\"_blank\" rel=\"noopener\">钉钉开放平台</a> → 企业内部应用 → AppKey / AppSecret。";
|
|
305
326
|
readonly tipWorkbuddyToken: "建议在终端运行 <code>open-im init</code> 完成 WorkBuddy 授权;或粘贴 CodeBuddy 登录后的 Token。";
|
|
327
|
+
readonly tipClawbotApiToken: "在终端运行 <code>open-im init</code> 选择 ClawBot 扫码登录,或从 ClawBot iLink 登录后复制 Token。";
|
|
306
328
|
};
|
|
307
329
|
};
|
|
@@ -4,7 +4,7 @@ export const PAGE_TEXTS = {
|
|
|
4
4
|
heroBadge: "open-im local control",
|
|
5
5
|
heroTitle: "Local bridge control.",
|
|
6
6
|
heroBody: "",
|
|
7
|
-
heroBodyFull: "One local bridge for Telegram, Feishu, QQ, WeWork, DingTalk, and
|
|
7
|
+
heroBodyFull: "One local bridge for Telegram, Feishu, QQ, WeWork, DingTalk, WorkBuddy, and ClawBot.",
|
|
8
8
|
heroKicker: "Local AI bridge",
|
|
9
9
|
langButton: "\u4e2d\u6587",
|
|
10
10
|
darkModeToggle: "Toggle dark mode",
|
|
@@ -56,6 +56,16 @@ export const PAGE_TEXTS = {
|
|
|
56
56
|
weworkSummary: "Corp ID and secret for enterprise delivery.",
|
|
57
57
|
dingtalkSummary: "Client credentials plus optional card template.",
|
|
58
58
|
workbuddySummary: "CodeBuddy OAuth for WeChat customer service.",
|
|
59
|
+
clawbotSummary: "WeChat iLink API via ClawBot.",
|
|
60
|
+
clawbotApiUrl: "API URL",
|
|
61
|
+
clawbotApiToken: "API Token (Bearer)",
|
|
62
|
+
clawbotHelp: 'Click "Scan QR Login" to authenticate via WeChat, or paste token manually.',
|
|
63
|
+
qrLoginBtn: "Scan QR Login",
|
|
64
|
+
qrLoginScanning: "Waiting for QR code scan...",
|
|
65
|
+
qrLoginSuccess: "Login successful! Credentials saved.",
|
|
66
|
+
qrLoginFailed: "Login failed",
|
|
67
|
+
qrLoginExpired: "QR code expired, please try again",
|
|
68
|
+
qrScanHint: "Use WeChat to scan the QR code",
|
|
59
69
|
platformCredentialsTitle: "Credentials",
|
|
60
70
|
platformAccessTitle: "Routing and access",
|
|
61
71
|
platformTestNote: "Checks required credentials against the platform.",
|
|
@@ -152,13 +162,14 @@ export const PAGE_TEXTS = {
|
|
|
152
162
|
tipWeworkCorp: "WeCom admin → app → view Corp ID (or smart-bot Bot ID) and Secret.",
|
|
153
163
|
tipDingtalkClient: '<a href="https://open-dev.dingtalk.com" target="_blank" rel="noopener">DingTalk Open Platform</a> → internal app → App Key & App Secret.',
|
|
154
164
|
tipWorkbuddyToken: 'Use terminal <code>open-im init</code> for WorkBuddy OAuth, or paste tokens from CodeBuddy login.',
|
|
165
|
+
tipClawbotApiToken: 'Run <code>open-im init</code> and select ClawBot to scan QR code, or paste token from ClawBot iLink login.',
|
|
155
166
|
},
|
|
156
167
|
zh: {
|
|
157
168
|
pageTitle: "open-im \u672c\u5730\u63a7\u5236\u53f0",
|
|
158
169
|
heroBadge: "open-im",
|
|
159
170
|
heroTitle: "\u672c\u5730\u6865\u63a5\u63a7\u5236\u53f0",
|
|
160
171
|
heroBody: "",
|
|
161
|
-
heroBodyFull: "\u4e00\u4e2a\u672c\u5730\u6865\u63a5\u5165\u53e3\uff0c\u7edf\u4e00\u8fde\u63a5 Telegram\u3001Feishu\u3001QQ\u3001WeWork\u3001DingTalk \u548c
|
|
172
|
+
heroBodyFull: "\u4e00\u4e2a\u672c\u5730\u6865\u63a5\u5165\u53e3\uff0c\u7edf\u4e00\u8fde\u63a5 Telegram\u3001Feishu\u3001QQ\u3001WeWork\u3001DingTalk\u3001WorkBuddy \u548c ClawBot\u3002",
|
|
162
173
|
heroKicker: "\u672c\u5730 AI \u6865\u63a5",
|
|
163
174
|
langButton: "EN",
|
|
164
175
|
darkModeToggle: "\u5207\u6362\u6697\u9ed1\u6a21\u5f0f",
|
|
@@ -210,6 +221,16 @@ export const PAGE_TEXTS = {
|
|
|
210
221
|
weworkSummary: "\u4f01\u4e1a\u5fae\u4fe1 Corp ID \u4e0e Secret\u3002",
|
|
211
222
|
dingtalkSummary: "\u9489\u9489 Client \u51ed\u8bc1\uff0c\u53ef\u9009\u914d\u7f6e\u5361\u7247\u6a21\u677f ID\u3002",
|
|
212
223
|
workbuddySummary: "CodeBuddy OAuth \u8fde\u63a5\u5fae\u4fe1\u5ba2\u670d\u3002",
|
|
224
|
+
clawbotSummary: "\u901a\u8fc7 ClawBot \u8fde\u63a5\u5fae\u4fe1 iLink API\u3002",
|
|
225
|
+
clawbotApiUrl: "API \u5730\u5740",
|
|
226
|
+
clawbotApiToken: "API Token (Bearer)",
|
|
227
|
+
clawbotHelp: '\u70b9\u51fb\u201c\u626b\u7801\u767b\u5f55\u201d\u901a\u8fc7\u5fae\u4fe1\u8ba4\u8bc1\uff0c\u6216\u624b\u52a8\u7c98\u8d34 Token\u3002',
|
|
228
|
+
qrLoginBtn: "\u626b\u7801\u767b\u5f55",
|
|
229
|
+
qrLoginScanning: "\u7b49\u5f85\u626b\u7801...",
|
|
230
|
+
qrLoginSuccess: "\u767b\u5f55\u6210\u529f\uff01\u51ed\u8bc1\u5df2\u4fdd\u5b58\u3002",
|
|
231
|
+
qrLoginFailed: "\u767b\u5f55\u5931\u8d25",
|
|
232
|
+
qrLoginExpired: "\u4e8c\u7ef4\u7801\u5df2\u8fc7\u671f\uff0c\u8bf7\u91cd\u8bd5",
|
|
233
|
+
qrScanHint: "\u8bf7\u4f7f\u7528\u5fae\u4fe1\u626b\u63cf\u4e8c\u7ef4\u7801",
|
|
213
234
|
platformCredentialsTitle: "\u51ed\u8bc1",
|
|
214
235
|
platformAccessTitle: "\u8def\u7531\u4e0e\u8bbf\u95ee",
|
|
215
236
|
platformTestNote: "\u53ea\u4f1a\u68c0\u67e5\u8be5\u5e73\u53f0\u7684\u5fc5\u586b\u51ed\u8bc1\u662f\u5426\u53ef\u7528\u3002",
|
|
@@ -303,5 +324,6 @@ export const PAGE_TEXTS = {
|
|
|
303
324
|
tipWeworkCorp: "\u4f01\u4e1a\u5fae\u4fe1\u7ba1\u7406\u540e\u53f0 \u2192 \u5e94\u7528 \u2192 \u67e5\u7701 Corp ID / \u667a\u80fd\u673a\u5668\u4eba Bot ID \u4e0e Secret\u3002",
|
|
304
325
|
tipDingtalkClient: '<a href="https://open-dev.dingtalk.com" target="_blank" rel="noopener">\u9489\u9489\u5f00\u653e\u5e73\u53f0</a> \u2192 \u4f01\u4e1a\u5185\u90e8\u5e94\u7528 \u2192 AppKey / AppSecret\u3002',
|
|
305
326
|
tipWorkbuddyToken: "\u5efa\u8bae\u5728\u7ec8\u7aef\u8fd0\u884c <code>open-im init</code> \u5b8c\u6210 WorkBuddy \u6388\u6743\uff1b\u6216\u7c98\u8d34 CodeBuddy \u767b\u5f55\u540e\u7684 Token\u3002",
|
|
327
|
+
tipClawbotApiToken: '\u5728\u7ec8\u7aef\u8fd0\u884c <code>open-im init</code> \u9009\u62e9 ClawBot \u626b\u7801\u767b\u5f55\uff0c\u6216\u4ece ClawBot iLink \u767b\u5f55\u540e\u590d\u5236 Token\u3002',
|
|
306
328
|
}
|
|
307
329
|
};
|
package/dist/config-web.js
CHANGED
|
@@ -163,6 +163,7 @@ export function getHealthPlatformSnapshot(file, env = process.env) {
|
|
|
163
163
|
const fileWework = file.platforms?.wework;
|
|
164
164
|
const fileDingtalk = file.platforms?.dingtalk;
|
|
165
165
|
const fileWorkbuddy = file.platforms?.workbuddy;
|
|
166
|
+
const fileClawbot = file.platforms?.clawbot;
|
|
166
167
|
const telegramBotToken = env.TELEGRAM_BOT_TOKEN ?? fileTelegram?.botToken ?? file.telegramBotToken;
|
|
167
168
|
const feishuAppId = env.FEISHU_APP_ID ?? fileFeishu?.appId ?? file.feishuAppId;
|
|
168
169
|
const feishuAppSecret = env.FEISHU_APP_SECRET ?? fileFeishu?.appSecret ?? file.feishuAppSecret;
|
|
@@ -212,6 +213,12 @@ export function getHealthPlatformSnapshot(file, env = process.env) {
|
|
|
212
213
|
healthy: !!(workbuddyAccessToken && workbuddyRefreshToken && workbuddyUserId),
|
|
213
214
|
message: workbuddyAccessToken && workbuddyRefreshToken && workbuddyUserId ? "OAuth credentials configured" : "Missing credentials",
|
|
214
215
|
},
|
|
216
|
+
clawbot: {
|
|
217
|
+
configured: !!fileClawbot?.apiToken,
|
|
218
|
+
enabled: !!fileClawbot?.apiToken && fileClawbot?.enabled !== false,
|
|
219
|
+
healthy: !!fileClawbot?.apiToken,
|
|
220
|
+
message: fileClawbot?.apiToken ? "API Token configured" : "Missing API Token",
|
|
221
|
+
},
|
|
215
222
|
};
|
|
216
223
|
}
|
|
217
224
|
function splitCsv(value) {
|
|
@@ -325,6 +332,13 @@ function buildInitialPayload(file) {
|
|
|
325
332
|
baseUrl: file.platforms?.workbuddy?.baseUrl ?? "",
|
|
326
333
|
allowedUserIds: (file.platforms?.workbuddy?.allowedUserIds ?? []).join(", "),
|
|
327
334
|
},
|
|
335
|
+
clawbot: {
|
|
336
|
+
enabled: file.platforms?.clawbot?.enabled ?? Boolean(file.platforms?.clawbot?.apiToken),
|
|
337
|
+
aiCommand: normalizeAiCommand(file.platforms?.clawbot?.aiCommand, "claude"),
|
|
338
|
+
apiUrl: file.platforms?.clawbot?.apiUrl ?? "http://127.0.0.1:26322",
|
|
339
|
+
apiToken: maskSecret(file.platforms?.clawbot?.apiToken),
|
|
340
|
+
allowedUserIds: (file.platforms?.clawbot?.allowedUserIds ?? []).join(", "),
|
|
341
|
+
},
|
|
328
342
|
},
|
|
329
343
|
ai: {
|
|
330
344
|
claudeWorkDir: file.tools?.claude?.workDir ?? process.cwd(),
|
|
@@ -388,6 +402,8 @@ function validatePayload(payload) {
|
|
|
388
402
|
errors.push("WorkBuddy refresh token is required.");
|
|
389
403
|
if (payload.platforms.workbuddy.enabled && !clean(payload.platforms.workbuddy.userId))
|
|
390
404
|
errors.push("WorkBuddy user ID is required.");
|
|
405
|
+
if (payload.platforms.clawbot.enabled && !clean(payload.platforms.clawbot.apiToken))
|
|
406
|
+
errors.push("ClawBot API token is required.");
|
|
391
407
|
if (!clean(payload.ai.claudeWorkDir))
|
|
392
408
|
errors.push("Default work directory is required.");
|
|
393
409
|
return errors;
|
|
@@ -447,6 +463,11 @@ function validateConfigForPlatform(platform, config) {
|
|
|
447
463
|
errors.push("WorkBuddy user ID is required and must be a non-empty string.");
|
|
448
464
|
}
|
|
449
465
|
break;
|
|
466
|
+
case "clawbot":
|
|
467
|
+
if (!c.apiToken || typeof c.apiToken !== "string" || !clean(c.apiToken)) {
|
|
468
|
+
errors.push("ClawBot API token is required and must be a non-empty string.");
|
|
469
|
+
}
|
|
470
|
+
break;
|
|
450
471
|
default:
|
|
451
472
|
errors.push(`Unknown platform: ${platform}`);
|
|
452
473
|
}
|
|
@@ -476,6 +497,7 @@ function createProbeConfig(values) {
|
|
|
476
497
|
weworkAllowedUserIds: [],
|
|
477
498
|
dingtalkAllowedUserIds: [],
|
|
478
499
|
workbuddyAllowedUserIds: [],
|
|
500
|
+
clawbotAllowedUserIds: [],
|
|
479
501
|
codexCliPath: "codex",
|
|
480
502
|
claudeWorkDir: process.cwd(),
|
|
481
503
|
claudeSessionIdleTtlMinutes: 30,
|
|
@@ -598,6 +620,29 @@ async function probeWorkBuddy(config) {
|
|
|
598
620
|
}
|
|
599
621
|
return "WorkBuddy credentials are valid.";
|
|
600
622
|
}
|
|
623
|
+
async function probeClawBot(config) {
|
|
624
|
+
const apiUrl = clean(String(config.apiUrl ?? "http://127.0.0.1:26322"));
|
|
625
|
+
const apiToken = clean(String(config.apiToken ?? ""));
|
|
626
|
+
if (!apiToken)
|
|
627
|
+
throw new Error("ClawBot API token is required.");
|
|
628
|
+
const { randomUUID } = await import('node:crypto');
|
|
629
|
+
const response = await fetch(`${apiUrl}/ilink/bot/getupdates?timeout=1&bot_token=${encodeURIComponent(apiToken)}`, {
|
|
630
|
+
headers: {
|
|
631
|
+
'Content-Type': 'application/json',
|
|
632
|
+
'AuthorizationType': 'ilink_bot_token',
|
|
633
|
+
'iLink-App-Id': 'bot',
|
|
634
|
+
'iLink-App-ClientVersion': '131588',
|
|
635
|
+
'X-WECHAT-UIN': randomUUID(),
|
|
636
|
+
},
|
|
637
|
+
signal: AbortSignal.timeout(TEST_TIMEOUT_MS),
|
|
638
|
+
});
|
|
639
|
+
const body = await readJsonResponse(response);
|
|
640
|
+
const ok = body.ok === true || body.ret === 0 || body.ret === '0';
|
|
641
|
+
if (!response.ok || !ok) {
|
|
642
|
+
throw new Error(String(body.error ?? body.description ?? `HTTP ${response.status}`));
|
|
643
|
+
}
|
|
644
|
+
return "ClawBot API reachable.";
|
|
645
|
+
}
|
|
601
646
|
export async function testPlatformConfig(platform, config) {
|
|
602
647
|
const errors = validateConfigForPlatform(platform, config);
|
|
603
648
|
if (errors.length > 0) {
|
|
@@ -616,6 +661,8 @@ export async function testPlatformConfig(platform, config) {
|
|
|
616
661
|
return probeDingTalk(config);
|
|
617
662
|
case "workbuddy":
|
|
618
663
|
return probeWorkBuddy(config);
|
|
664
|
+
case "clawbot":
|
|
665
|
+
return probeClawBot(config);
|
|
619
666
|
default:
|
|
620
667
|
throw new Error(`Unknown platform: ${platform}`);
|
|
621
668
|
}
|
|
@@ -711,6 +758,14 @@ function toFileConfig(payload, existing) {
|
|
|
711
758
|
baseUrl: clean(payload.platforms.workbuddy.baseUrl),
|
|
712
759
|
allowedUserIds: splitCsv(payload.platforms.workbuddy.allowedUserIds),
|
|
713
760
|
},
|
|
761
|
+
clawbot: {
|
|
762
|
+
...existing.platforms?.clawbot,
|
|
763
|
+
enabled: payload.platforms.clawbot.enabled,
|
|
764
|
+
aiCommand: persistedPlatformAi(payload.platforms.clawbot.aiCommand),
|
|
765
|
+
apiUrl: clean(payload.platforms.clawbot.apiUrl) ?? "http://127.0.0.1:26322",
|
|
766
|
+
apiToken: resolveSecret(payload.platforms.clawbot.apiToken, existing.platforms?.clawbot?.apiToken),
|
|
767
|
+
allowedUserIds: splitCsv(payload.platforms.clawbot.allowedUserIds),
|
|
768
|
+
},
|
|
714
769
|
},
|
|
715
770
|
};
|
|
716
771
|
}
|
|
@@ -1071,6 +1126,30 @@ export async function startWebConfigServer(options) {
|
|
|
1071
1126
|
}
|
|
1072
1127
|
return;
|
|
1073
1128
|
}
|
|
1129
|
+
if (request.method === "POST" && requestUrl.pathname === "/api/clawbot/qr-login/start") {
|
|
1130
|
+
try {
|
|
1131
|
+
const { startQRLogin } = await import("./clawbot/qr-login.js");
|
|
1132
|
+
const session = await startQRLogin();
|
|
1133
|
+
json(response, 200, { success: true, qrcodeUrl: session.qrcodeUrl, sessionKey: session.sessionKey }, request);
|
|
1134
|
+
}
|
|
1135
|
+
catch (error) {
|
|
1136
|
+
json(response, 500, { success: false, error: toErrorMessage(error) }, request);
|
|
1137
|
+
}
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
if (request.method === "POST" && requestUrl.pathname === "/api/clawbot/qr-login/wait") {
|
|
1141
|
+
try {
|
|
1142
|
+
const body = await readJson(request);
|
|
1143
|
+
const { waitForQRLogin } = await import("./clawbot/qr-login.js");
|
|
1144
|
+
const session = { sessionKey: body.sessionKey, qrcode: body.qrcode, qrcodeUrl: body.qrcodeUrl, startedAt: Date.now() };
|
|
1145
|
+
const result = await waitForQRLogin(session);
|
|
1146
|
+
json(response, 200, { success: result.connected, botToken: result.botToken, baseUrl: result.baseUrl, userId: result.userId, message: result.message }, request);
|
|
1147
|
+
}
|
|
1148
|
+
catch (error) {
|
|
1149
|
+
json(response, 500, { success: false, error: toErrorMessage(error) }, request);
|
|
1150
|
+
}
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1074
1153
|
if (request.method === "GET" && tryServeDashboardStatic(requestUrl, request, response, mergeCors)) {
|
|
1075
1154
|
return;
|
|
1076
1155
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { Platform, AiCommand, Config, FilePlatformTelegram, FilePlatformFeishu, FilePlatformQQ, FilePlatformWechat, FilePlatformWework, FilePlatformDingtalk, FilePlatformWorkBuddy, FileToolClaude, FileToolCodex, FileToolCodeBuddy, FileConfig, } from './config/types.js';
|
|
1
|
+
export type { Platform, AiCommand, Config, FilePlatformTelegram, FilePlatformFeishu, FilePlatformQQ, FilePlatformWechat, FilePlatformWework, FilePlatformDingtalk, FilePlatformWorkBuddy, FilePlatformClawbot, FileToolClaude, FileToolCodex, FileToolCodeBuddy, FileConfig, } from './config/types.js';
|
|
2
2
|
import type { Platform, AiCommand, Config } from './config/types.js';
|
|
3
3
|
export { CONFIG_PATH, loadFileConfig, saveFileConfig, getClaudeConfigHome, getClaudeSdkRuntimeIssue, hasCodeBuddyAuthIndicators, loadClaudeSettingsEnv, saveClaudeSettingsEnv, normalizeAiCommand, hasCodexAuth, parseCommaSeparated, CLAUDE_AUTH_ENV_KEYS, refreshClaudeEnvToProcess, processEnvForNonClaudeCliChild, CODEX_AUTH_PATHS, } from './config/file-io.js';
|
|
4
4
|
/** 检测是否需要交互式配置(无 token 且无环境变量) */
|