@wu529778790/open-im 1.11.10-beta.1 → 1.11.10-beta.2
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getConfiguredAiCommands } from '../config.js';
|
|
1
|
+
import { getConfiguredAiCommands, loadConfig } from '../config.js';
|
|
2
2
|
import { ClaudeSDKAdapter } from './claude-sdk-adapter.js';
|
|
3
3
|
import { CodexAdapter } from './codex-adapter.js';
|
|
4
4
|
import { CodeBuddyAdapter } from './codebuddy-adapter.js';
|
|
@@ -37,7 +37,32 @@ export function initAdapters(config) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
export function getAdapter(aiCommand) {
|
|
40
|
-
|
|
40
|
+
const existing = adapters.get(aiCommand);
|
|
41
|
+
if (existing)
|
|
42
|
+
return existing;
|
|
43
|
+
// 懒加载:启动时未初始化的工具(例如启动用 claude,运行中切到 opencode)
|
|
44
|
+
// 按需创建并缓存,避免返回 undefined 导致消息处理失败。
|
|
45
|
+
const factory = ADAPTER_FACTORIES[aiCommand];
|
|
46
|
+
if (!factory)
|
|
47
|
+
return undefined;
|
|
48
|
+
let config;
|
|
49
|
+
try {
|
|
50
|
+
config = loadConfig();
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
log.error(`Lazy-create adapter "${aiCommand}" failed to load config:`, err);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const adapter = factory(config);
|
|
57
|
+
adapters.set(aiCommand, adapter);
|
|
58
|
+
log.info(`${aiCommand} adapter lazy-created (was not enabled at startup)`);
|
|
59
|
+
// SDK 工具需要懒启动 server,与 initAdapters 行为一致
|
|
60
|
+
if (aiCommand === 'opencode' && !isOpencodeRunning()) {
|
|
61
|
+
startOpencode().catch((err) => {
|
|
62
|
+
log.warn(`OpenCode SDK server lazy-start failed (will retry on first use): ${err}`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return adapter;
|
|
41
66
|
}
|
|
42
67
|
export function cleanupAdapters() {
|
|
43
68
|
ClaudeSDKAdapter.destroy();
|
package/dist/commands/handler.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { resolvePlatformAiCommand } from '../config.js';
|
|
1
|
+
import { resolvePlatformAiCommand, loadConfig, loadFileConfig, saveFileConfig, } from '../config.js';
|
|
2
2
|
import { escapePathForMarkdown } from '../shared/utils.js';
|
|
3
3
|
import { getAutopilotPendingStatus } from '../shared/ai-task.js';
|
|
4
4
|
import { TERMINAL_ONLY_COMMANDS } from '../constants.js';
|
|
5
5
|
import { createLogger } from '../logger.js';
|
|
6
6
|
import { ClaudeSDKAdapter } from '../adapters/claude-sdk-adapter.js';
|
|
7
|
-
import { AI_TOOL_BY_ID } from '../adapters/tool-registry.js';
|
|
7
|
+
import { AI_TOOL_BY_ID, AI_TOOLS, isAiCommand } from '../adapters/tool-registry.js';
|
|
8
8
|
const log = createLogger('Commands');
|
|
9
9
|
function formatRelativeTime(ts) {
|
|
10
10
|
const sec = Math.floor((Date.now() - ts) / 1000);
|
|
@@ -104,6 +104,8 @@ export class CommandHandler {
|
|
|
104
104
|
return this.handlePwd(chatId, userId);
|
|
105
105
|
if (t === '/status')
|
|
106
106
|
return this.handleStatus(chatId, userId, platform);
|
|
107
|
+
if (t === '/a' || t.startsWith('/a '))
|
|
108
|
+
return this.handleSwitchAi(chatId, t.slice(2).trim(), platform);
|
|
107
109
|
if (t === '/autopilot')
|
|
108
110
|
return this.handleAutopilotStatus(chatId, userId);
|
|
109
111
|
// 快捷命令 — 直接发送预设 prompt 给 AI
|
|
@@ -182,6 +184,7 @@ export class CommandHandler {
|
|
|
182
184
|
'/plugins - 查看已安装插件',
|
|
183
185
|
'/context - 查看上下文窗口占用',
|
|
184
186
|
'/status - 显示状态',
|
|
187
|
+
'/a [工具名] - 查看或切换 AI 工具(claude/codex/codebuddy/opencode)',
|
|
185
188
|
'/cd <路径> - 切换工作目录',
|
|
186
189
|
'/pwd - 当前工作目录',
|
|
187
190
|
'/autopilot - 查看限流自动恢复状态',
|
|
@@ -289,7 +292,9 @@ export class CommandHandler {
|
|
|
289
292
|
return true;
|
|
290
293
|
}
|
|
291
294
|
async handleStatus(chatId, userId, platform) {
|
|
292
|
-
|
|
295
|
+
// 重新读取配置,使 /a 切换后立即反映新工具(与 AI 请求路径一致)
|
|
296
|
+
const config = loadConfig();
|
|
297
|
+
const aiCommand = resolvePlatformAiCommand(config, platform);
|
|
293
298
|
const version = await this.getAiVersion(aiCommand);
|
|
294
299
|
const workDir = this.deps.sessionManager.getWorkDir(userId);
|
|
295
300
|
const convId = this.deps.sessionManager.getConvId(userId);
|
|
@@ -319,6 +324,45 @@ export class CommandHandler {
|
|
|
319
324
|
await this.replySender().sendTextReply(chatId, lines.join('\n'));
|
|
320
325
|
return true;
|
|
321
326
|
}
|
|
327
|
+
async handleSwitchAi(chatId, arg, platform) {
|
|
328
|
+
// 无参数:显示当前工具 + 可选列表
|
|
329
|
+
if (!arg) {
|
|
330
|
+
const current = resolvePlatformAiCommand(loadConfig(), platform);
|
|
331
|
+
const lines = ['🔄 切换 AI 工具:', '', `当前 (${platform}): ${current}`, '', '可选:'];
|
|
332
|
+
for (const tool of AI_TOOLS) {
|
|
333
|
+
const mark = tool.id === current ? ' (当前)' : '';
|
|
334
|
+
lines.push(` /a ${tool.id} - ${tool.label}${mark}`);
|
|
335
|
+
}
|
|
336
|
+
lines.push('', '用法: /a <工具名>');
|
|
337
|
+
await this.replySender().sendTextReply(chatId, lines.join('\n'));
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
// 校验工具名
|
|
341
|
+
if (!isAiCommand(arg)) {
|
|
342
|
+
const valid = AI_TOOLS.map((t) => t.id).join(', ');
|
|
343
|
+
await this.replySender().sendTextReply(chatId, `❌ 未知工具: ${arg}\n\n可选: ${valid}`);
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
// 持久化到 config.json(load→mutate→save 模式,与 saveKeepaliveConfig 一致)
|
|
347
|
+
try {
|
|
348
|
+
const file = loadFileConfig();
|
|
349
|
+
if (!file.platforms)
|
|
350
|
+
file.platforms = {};
|
|
351
|
+
// 所有平台配置都含 aiCommand?: AiCommand,按此最小契约写入即可
|
|
352
|
+
const platforms = file.platforms;
|
|
353
|
+
const platCfg = platforms[platform] ?? {};
|
|
354
|
+
platCfg.aiCommand = arg;
|
|
355
|
+
platforms[platform] = platCfg;
|
|
356
|
+
saveFileConfig(file);
|
|
357
|
+
const label = AI_TOOL_BY_ID[arg]?.label ?? arg;
|
|
358
|
+
await this.replySender().sendTextReply(chatId, `✅ 已将 ${platform} 的 AI 工具切换为 ${label} (${arg})\n下一条消息生效,无需重启。`);
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
log.error('Failed to switch aiCommand:', err);
|
|
362
|
+
await this.replySender().sendTextReply(chatId, '❌ 切换失败,请查看日志。');
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
322
366
|
async handleCd(chatId, userId, dir, _platform) {
|
|
323
367
|
// 如果 dir 为空,显示目录选择界面
|
|
324
368
|
if (!dir) {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* 4. Start typing indicator
|
|
12
12
|
* 5. Run AI task via runAITask with platform-specific callbacks
|
|
13
13
|
*/
|
|
14
|
-
import { resolvePlatformAiCommand } from '../config.js';
|
|
14
|
+
import { resolvePlatformAiCommand, loadConfig } from '../config.js';
|
|
15
15
|
import { getAdapter } from '../adapters/registry.js';
|
|
16
16
|
import { runAITask } from '../shared/ai-task.js';
|
|
17
17
|
import { createLogger } from '../logger.js';
|
|
@@ -27,10 +27,20 @@ const log = createLogger('PlatformAI');
|
|
|
27
27
|
* 5. run AI task with platform callbacks
|
|
28
28
|
*/
|
|
29
29
|
export function createPlatformAIRequestHandler(deps) {
|
|
30
|
-
const { platform, config, sessionManager, sender, throttleMs, runningTasks, minContentDeltaChars, taskKeyBuilder, onThinkingToText, extraInit, taskCallbacks, taskCallbacksFactory, } = deps;
|
|
30
|
+
const { platform, config: initialConfig, sessionManager, sender, throttleMs, runningTasks, minContentDeltaChars, taskKeyBuilder, onThinkingToText, extraInit, taskCallbacks, taskCallbacksFactory, } = deps;
|
|
31
31
|
async function handleAIRequest(params) {
|
|
32
32
|
const { userId, chatId, prompt, workDir, convId, replyToMessageId, signal } = params;
|
|
33
33
|
log.info(`[${platform}] AI request: userId=${userId}, chatId=${chatId}, promptLength=${prompt.length}`);
|
|
34
|
+
// 每条消息重新读取配置,使平台 aiCommand 的修改无需重启即可生效。
|
|
35
|
+
// loadFileConfig 有 mtime 缓存,文件未变时几乎零开销;变更后自动返回新值。
|
|
36
|
+
let config;
|
|
37
|
+
try {
|
|
38
|
+
config = loadConfig();
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
log.warn(`[${platform}] Failed to reload config, using startup snapshot:`, err);
|
|
42
|
+
config = initialConfig;
|
|
43
|
+
}
|
|
34
44
|
// 1. Resolve AI command and adapter
|
|
35
45
|
const aiCommand = resolvePlatformAiCommand(config, platform);
|
|
36
46
|
const toolAdapter = getAdapter(aiCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wu529778790/open-im",
|
|
3
|
-
"version": "1.11.10-beta.
|
|
3
|
+
"version": "1.11.10-beta.2",
|
|
4
4
|
"description": "Your AI coding assistant, in every chat app. Multi-platform IM bridge for Claude Code, Codex, and CodeBuddy.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|