@wu529778790/open-im 1.11.6 → 1.11.7-beta.0
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/dist/adapters/registry.js +15 -16
- package/dist/adapters/tool-registry.d.ts +51 -0
- package/dist/adapters/tool-registry.js +55 -0
- package/dist/commands/handler.js +7 -6
- package/dist/config/file-io.d.ts +2 -2
- package/dist/config/file-io.js +2 -6
- package/dist/config/types.d.ts +2 -1
- package/dist/config-web-page-i18n.d.ts +2 -0
- package/dist/config-web-page-i18n.js +2 -0
- package/dist/config-web.js +2 -4
- package/dist/config.js +33 -40
- package/dist/session/session-manager.d.ts +2 -1
- package/dist/session/session-manager.js +2 -1
- package/dist/setup.js +6 -1
- package/dist/shared/utils.js +7 -5
- package/package.json +1 -1
- package/web/dist/assets/{index-BhCQOCWI.js → index-yDVGC_84.js} +12 -12
- package/web/dist/index.html +1 -1
|
@@ -7,27 +7,26 @@ import { createLogger } from '../logger.js';
|
|
|
7
7
|
import { destroyAllLiveChildren } from '../shared/process-kill.js';
|
|
8
8
|
const log = createLogger('Registry');
|
|
9
9
|
const adapters = new Map();
|
|
10
|
+
/**
|
|
11
|
+
* 工具 id → adapter 工厂的映射。
|
|
12
|
+
* 新增工具在此加一行(id 与 tool-registry 的 id 对应)即可,无需改 initAdapters 逻辑。
|
|
13
|
+
*/
|
|
14
|
+
const ADAPTER_FACTORIES = {
|
|
15
|
+
claude: () => new ClaudeSDKAdapter(),
|
|
16
|
+
codex: (c) => new CodexAdapter(c.codexCliPath),
|
|
17
|
+
codebuddy: (c) => new CodeBuddyAdapter(c.codebuddyCliPath),
|
|
18
|
+
opencode: (c) => new OpenCodeAdapter(c.opencodeCliPath),
|
|
19
|
+
};
|
|
10
20
|
export function initAdapters(config) {
|
|
11
21
|
adapters.clear();
|
|
12
22
|
for (const aiCommand of getConfiguredAiCommands(config)) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
const factory = ADAPTER_FACTORIES[aiCommand];
|
|
24
|
+
if (!factory) {
|
|
25
|
+
log.warn(`No adapter factory registered for: ${aiCommand}`);
|
|
16
26
|
continue;
|
|
17
27
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
adapters.set('codex', new CodexAdapter(config.codexCliPath));
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (aiCommand === 'codebuddy') {
|
|
24
|
-
log.info('CodeBuddy CLI adapter enabled');
|
|
25
|
-
adapters.set('codebuddy', new CodeBuddyAdapter(config.codebuddyCliPath));
|
|
26
|
-
}
|
|
27
|
-
if (aiCommand === 'opencode') {
|
|
28
|
-
log.info('OpenCode CLI adapter enabled');
|
|
29
|
-
adapters.set('opencode', new OpenCodeAdapter(config.opencodeCliPath));
|
|
30
|
-
}
|
|
28
|
+
log.info(`${aiCommand} adapter enabled`);
|
|
29
|
+
adapters.set(aiCommand, factory(config));
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
32
|
export function getAdapter(aiCommand) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI 工具注册表 — 数据驱动的工具定义,消除散落在各处的
|
|
3
|
+
* `'claude' | 'codex' | 'codebuddy' | 'opencode'` 字面量联合。
|
|
4
|
+
*
|
|
5
|
+
* 新增一个 AI 工具只需在此数组加一条,`AiCommand` 类型、验证函数、
|
|
6
|
+
* 显示名映射均自动派生。
|
|
7
|
+
*
|
|
8
|
+
* 与前端 `web/src/tool-definitions.ts` 保持同构(后者面向 UI)。
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* 单个 AI 工具的定义。
|
|
12
|
+
*/
|
|
13
|
+
export interface ToolDefinition {
|
|
14
|
+
/** 工具唯一标识,即 config.json 里 `platforms.*.aiCommand` 的取值 */
|
|
15
|
+
id: string;
|
|
16
|
+
/** 人类可读名称(用于通知、/status 等) */
|
|
17
|
+
label: string;
|
|
18
|
+
/**
|
|
19
|
+
* 进程内 SDK(Claude) vs spawn 的 CLI(其余)。
|
|
20
|
+
* 决定 registry 是否传 cliPath、handler 的 /version 是否查 CLI 路径。
|
|
21
|
+
*/
|
|
22
|
+
isSdk?: boolean;
|
|
23
|
+
/** Config 中存放 CLI 路径的字段名(cliDefault 为空时该字段也用于 Windows .cmd 解析) */
|
|
24
|
+
cliPathField?: 'codexCliPath' | 'codebuddyCliPath' | 'opencodeCliPath';
|
|
25
|
+
/** CLI 默认可执行名(非绝对路径时作为 PATH 查找名) */
|
|
26
|
+
cliDefault?: string;
|
|
27
|
+
/** 是否支持 HTTP/HTTPS 代理(目前仅 codex) */
|
|
28
|
+
needsProxy?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 全部已注册的 AI 工具。新增工具在此追加一条即可。
|
|
32
|
+
*/
|
|
33
|
+
export declare const AI_TOOLS: readonly ToolDefinition[];
|
|
34
|
+
/** 全部工具 id 的字面量元组,用于派生 `AiCommand` 联合类型 */
|
|
35
|
+
export declare const AI_TOOL_IDS: { readonly [K in keyof typeof AI_TOOLS]: ToolDefinition["id"]; } & readonly string[];
|
|
36
|
+
/**
|
|
37
|
+
* AI 工具命令的联合类型。
|
|
38
|
+
* 从 `AI_TOOLS` 派生,而非手写字面量,保证注册表是唯一真相源。
|
|
39
|
+
*/
|
|
40
|
+
export type AiCommand = (typeof AI_TOOLS)[number]['id'];
|
|
41
|
+
/** 按 id 索引的工具定义映射 */
|
|
42
|
+
export declare const AI_TOOL_BY_ID: Readonly<Record<string, ToolDefinition>>;
|
|
43
|
+
/**
|
|
44
|
+
* 值是否为已注册的 AI 命令。
|
|
45
|
+
*/
|
|
46
|
+
export declare function isAiCommand(value: unknown): value is AiCommand;
|
|
47
|
+
/**
|
|
48
|
+
* 规范化为合法 AiCommand;非法值回落到 fallback。
|
|
49
|
+
* 收敛此前散落在 config.ts / file-io.ts / config-web.ts / setup.ts 的重复实现。
|
|
50
|
+
*/
|
|
51
|
+
export declare function normalizeAiCommand(value: unknown, fallback: AiCommand): AiCommand;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI 工具注册表 — 数据驱动的工具定义,消除散落在各处的
|
|
3
|
+
* `'claude' | 'codex' | 'codebuddy' | 'opencode'` 字面量联合。
|
|
4
|
+
*
|
|
5
|
+
* 新增一个 AI 工具只需在此数组加一条,`AiCommand` 类型、验证函数、
|
|
6
|
+
* 显示名映射均自动派生。
|
|
7
|
+
*
|
|
8
|
+
* 与前端 `web/src/tool-definitions.ts` 保持同构(后者面向 UI)。
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* 全部已注册的 AI 工具。新增工具在此追加一条即可。
|
|
12
|
+
*/
|
|
13
|
+
export const AI_TOOLS = [
|
|
14
|
+
{
|
|
15
|
+
id: 'claude',
|
|
16
|
+
label: 'Claude Code',
|
|
17
|
+
isSdk: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'codex',
|
|
21
|
+
label: 'Codex',
|
|
22
|
+
cliPathField: 'codexCliPath',
|
|
23
|
+
cliDefault: 'codex',
|
|
24
|
+
needsProxy: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'codebuddy',
|
|
28
|
+
label: 'CodeBuddy',
|
|
29
|
+
cliPathField: 'codebuddyCliPath',
|
|
30
|
+
cliDefault: 'codebuddy',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'opencode',
|
|
34
|
+
label: 'OpenCode',
|
|
35
|
+
cliPathField: 'opencodeCliPath',
|
|
36
|
+
cliDefault: 'opencode',
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
/** 全部工具 id 的字面量元组,用于派生 `AiCommand` 联合类型 */
|
|
40
|
+
export const AI_TOOL_IDS = Object.freeze(AI_TOOLS.map((t) => t.id));
|
|
41
|
+
/** 按 id 索引的工具定义映射 */
|
|
42
|
+
export const AI_TOOL_BY_ID = Object.freeze(Object.fromEntries(AI_TOOLS.map((t) => [t.id, t])));
|
|
43
|
+
/**
|
|
44
|
+
* 值是否为已注册的 AI 命令。
|
|
45
|
+
*/
|
|
46
|
+
export function isAiCommand(value) {
|
|
47
|
+
return typeof value === 'string' && value in AI_TOOL_BY_ID;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 规范化为合法 AiCommand;非法值回落到 fallback。
|
|
51
|
+
* 收敛此前散落在 config.ts / file-io.ts / config-web.ts / setup.ts 的重复实现。
|
|
52
|
+
*/
|
|
53
|
+
export function normalizeAiCommand(value, fallback) {
|
|
54
|
+
return isAiCommand(value) ? value : fallback;
|
|
55
|
+
}
|
package/dist/commands/handler.js
CHANGED
|
@@ -3,6 +3,7 @@ import { escapePathForMarkdown } from '../shared/utils.js';
|
|
|
3
3
|
import { TERMINAL_ONLY_COMMANDS } from '../constants.js';
|
|
4
4
|
import { createLogger } from '../logger.js';
|
|
5
5
|
import { ClaudeSDKAdapter } from '../adapters/claude-sdk-adapter.js';
|
|
6
|
+
import { AI_TOOL_BY_ID } from '../adapters/tool-registry.js';
|
|
6
7
|
const log = createLogger('Commands');
|
|
7
8
|
function formatRelativeTime(ts) {
|
|
8
9
|
const sec = Math.floor((Date.now() - ts) / 1000);
|
|
@@ -457,14 +458,14 @@ export class CommandHandler {
|
|
|
457
458
|
return true;
|
|
458
459
|
}
|
|
459
460
|
getAiVersion(aiCommand) {
|
|
460
|
-
|
|
461
|
+
const def = AI_TOOL_BY_ID[aiCommand];
|
|
462
|
+
if (!def || def.isSdk) {
|
|
461
463
|
return Promise.resolve('SDK Mode');
|
|
462
464
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
: this.deps.config.codebuddyCliPath;
|
|
465
|
+
// 通过 registry 的 cliPathField 查 config,消除第二处 tool→cliPath 映射。
|
|
466
|
+
const cmd = def.cliPathField ? this.deps.config[def.cliPathField] : undefined;
|
|
467
|
+
if (!cmd)
|
|
468
|
+
return Promise.resolve('未知');
|
|
468
469
|
return new Promise((resolve) => {
|
|
469
470
|
execFile(cmd, ['--version'], { timeout: 5000 }, (err, stdout) => {
|
|
470
471
|
resolve(err ? '未知' : (stdout?.toString().trim() || '未知'));
|
package/dist/config/file-io.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileConfig } from './types.js';
|
|
2
2
|
export declare const CONFIG_PATH: string;
|
|
3
3
|
export declare const CODEX_AUTH_PATHS: string[];
|
|
4
|
+
export { normalizeAiCommand, isAiCommand } from '../adapters/tool-registry.js';
|
|
4
5
|
/** Claude 认证相关的环境变量 key 列表 */
|
|
5
6
|
export declare const CLAUDE_AUTH_ENV_KEYS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_BASE_URL", "ANTHROPIC_MODEL"];
|
|
6
7
|
/**
|
|
@@ -12,7 +13,6 @@ export declare function saveFileConfig(raw: FileConfig): void;
|
|
|
12
13
|
export declare function getClaudeConfigHome(): string;
|
|
13
14
|
export declare function loadClaudeSettingsEnv(): Record<string, string>;
|
|
14
15
|
export declare function saveClaudeSettingsEnv(env: Record<string, string>): void;
|
|
15
|
-
export declare function normalizeAiCommand(value: unknown, fallback: AiCommand): AiCommand;
|
|
16
16
|
export declare function hasCodexAuth(): boolean;
|
|
17
17
|
export declare function hasCodeBuddyAuthIndicators(): boolean;
|
|
18
18
|
export declare function getClaudeSdkRuntimeIssue(): string | null;
|
package/dist/config/file-io.js
CHANGED
|
@@ -21,7 +21,8 @@ const OLD_ROOT_KEYS = [
|
|
|
21
21
|
'claudeTimeoutMs',
|
|
22
22
|
'claudeModel',
|
|
23
23
|
];
|
|
24
|
-
|
|
24
|
+
// AI 命令的真相源在 tool-registry;此处仅 re-export 以保持调用点兼容。
|
|
25
|
+
export { normalizeAiCommand, isAiCommand } from '../adapters/tool-registry.js';
|
|
25
26
|
const require = createRequire(import.meta.url);
|
|
26
27
|
/** Claude 认证相关的环境变量 key 列表 */
|
|
27
28
|
export const CLAUDE_AUTH_ENV_KEYS = [
|
|
@@ -221,11 +222,6 @@ export function saveClaudeSettingsEnv(env) {
|
|
|
221
222
|
throw new Error(`Failed to save Claude settings: ${error instanceof Error ? error.message : String(error)}`);
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
|
-
export function normalizeAiCommand(value, fallback) {
|
|
225
|
-
return typeof value === 'string' && AI_COMMANDS.includes(value)
|
|
226
|
-
? value
|
|
227
|
-
: fallback;
|
|
228
|
-
}
|
|
229
225
|
export function hasCodexAuth() {
|
|
230
226
|
if (process.env.OPENAI_API_KEY)
|
|
231
227
|
return true;
|
package/dist/config/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LogLevel } from '../logger.js';
|
|
2
|
+
import type { AiCommand } from '../adapters/tool-registry.js';
|
|
3
|
+
export type { AiCommand };
|
|
2
4
|
export type Platform = 'clawbot' | 'dingtalk' | 'feishu' | 'qq' | 'telegram' | 'wework' | 'workbuddy';
|
|
3
|
-
export type AiCommand = 'claude' | 'codex' | 'codebuddy' | 'opencode';
|
|
4
5
|
export interface Config {
|
|
5
6
|
enabledPlatforms: Platform[];
|
|
6
7
|
telegramBotToken?: string;
|
|
@@ -109,6 +109,7 @@ export declare const PAGE_TEXTS: {
|
|
|
109
109
|
readonly claudeCli: "Claude CLI path";
|
|
110
110
|
readonly codexCli: "Codex CLI path";
|
|
111
111
|
readonly codebuddyCli: "CodeBuddy CLI path";
|
|
112
|
+
readonly opencodeCli: "OpenCode CLI path";
|
|
112
113
|
readonly codexProxy: "Codex proxy";
|
|
113
114
|
readonly codexApiKey: "OPENAI_API_KEY";
|
|
114
115
|
readonly codexApiKeyTip: "Set the OpenAI API key used by Codex. You can also edit the auth file below.";
|
|
@@ -319,6 +320,7 @@ export declare const PAGE_TEXTS: {
|
|
|
319
320
|
readonly claudeCli: "Claude CLI 路径";
|
|
320
321
|
readonly codexCli: "Codex CLI 路径";
|
|
321
322
|
readonly codebuddyCli: "CodeBuddy CLI 路径";
|
|
323
|
+
readonly opencodeCli: "OpenCode CLI 路径";
|
|
322
324
|
readonly codexProxy: "Codex 代理";
|
|
323
325
|
readonly codexApiKey: "OPENAI_API_KEY";
|
|
324
326
|
readonly codexApiKeyTip: "设置 Codex 使用的 OpenAI API Key。也可以在下方编辑 auth 文件。";
|
|
@@ -109,6 +109,7 @@ export const PAGE_TEXTS = {
|
|
|
109
109
|
claudeCli: "Claude CLI path",
|
|
110
110
|
codexCli: "Codex CLI path",
|
|
111
111
|
codebuddyCli: "CodeBuddy CLI path",
|
|
112
|
+
opencodeCli: "OpenCode CLI path",
|
|
112
113
|
codexProxy: "Codex proxy",
|
|
113
114
|
codexApiKey: "OPENAI_API_KEY",
|
|
114
115
|
codexApiKeyTip: "Set the OpenAI API key used by Codex. You can also edit the auth file below.",
|
|
@@ -320,6 +321,7 @@ export const PAGE_TEXTS = {
|
|
|
320
321
|
claudeCli: "Claude CLI \u8def\u5f84",
|
|
321
322
|
codexCli: "Codex CLI \u8def\u5f84",
|
|
322
323
|
codebuddyCli: "CodeBuddy CLI \u8def\u5f84",
|
|
324
|
+
opencodeCli: "OpenCode CLI \u8def\u5f84",
|
|
323
325
|
codexProxy: "Codex \u4ee3\u7406",
|
|
324
326
|
codexApiKey: "OPENAI_API_KEY",
|
|
325
327
|
codexApiKeyTip: "\u8bbe\u7f6e Codex \u4f7f\u7528\u7684 OpenAI API Key\u3002\u4e5f\u53ef\u4ee5\u5728\u4e0b\u65b9\u7f16\u8f91 auth \u6587\u4ef6\u3002",
|
package/dist/config-web.js
CHANGED
|
@@ -233,10 +233,8 @@ function clean(value) {
|
|
|
233
233
|
return trimmed ? trimmed : undefined;
|
|
234
234
|
}
|
|
235
235
|
function persistedPlatformAi(v) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return c;
|
|
239
|
-
return "claude";
|
|
236
|
+
// 经 registry 规范化;此前硬编码只认 codex/codebuddy/claude,会把 opencode 降级为 claude。
|
|
237
|
+
return normalizeAiCommand(clean(v), "claude");
|
|
240
238
|
}
|
|
241
239
|
function isMasked(value) {
|
|
242
240
|
return typeof value === "string" && value.includes("****");
|
package/dist/config.js
CHANGED
|
@@ -10,23 +10,28 @@ import { join, isAbsolute } from 'node:path';
|
|
|
10
10
|
import { createLogger } from './logger.js';
|
|
11
11
|
import { APP_HOME, } from './constants.js';
|
|
12
12
|
const log = createLogger('config');
|
|
13
|
+
import { isAiCommand, AI_TOOL_BY_ID } from './adapters/tool-registry.js';
|
|
14
|
+
/**
|
|
15
|
+
* 按平台 id 从 FileConfig.platforms 取出该平台的 aiCommand 原始值。
|
|
16
|
+
* platforms 是固定 shape 接口(每个平台一个字段),无法直接索引,这里集中映射。
|
|
17
|
+
*/
|
|
18
|
+
function getFilePlatformAiRaw(file, platform) {
|
|
19
|
+
const pf = file.platforms;
|
|
20
|
+
switch (platform) {
|
|
21
|
+
case 'telegram': return pf?.telegram?.aiCommand;
|
|
22
|
+
case 'feishu': return pf?.feishu?.aiCommand;
|
|
23
|
+
case 'qq': return pf?.qq?.aiCommand;
|
|
24
|
+
case 'wework': return pf?.wework?.aiCommand;
|
|
25
|
+
case 'dingtalk': return pf?.dingtalk?.aiCommand;
|
|
26
|
+
case 'clawbot': return pf?.clawbot?.aiCommand;
|
|
27
|
+
case 'workbuddy': return pf?.workbuddy?.aiCommand;
|
|
28
|
+
default: return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
13
31
|
/** 分渠道 AI 工具:未配置时使用 AI_COMMAND / 旧版根级 aiCommand / 默认 claude */
|
|
14
32
|
function resolveFilePlatformAi(file, platform) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
? pf?.telegram?.aiCommand
|
|
18
|
-
: platform === 'feishu'
|
|
19
|
-
? pf?.feishu?.aiCommand
|
|
20
|
-
: platform === 'qq'
|
|
21
|
-
? pf?.qq?.aiCommand
|
|
22
|
-
: platform === 'wework'
|
|
23
|
-
? pf?.wework?.aiCommand
|
|
24
|
-
: platform === 'dingtalk'
|
|
25
|
-
? pf?.dingtalk?.aiCommand
|
|
26
|
-
: platform === 'clawbot'
|
|
27
|
-
? pf?.clawbot?.aiCommand
|
|
28
|
-
: pf?.workbuddy?.aiCommand;
|
|
29
|
-
return normalizeAiCommand(raw ?? process.env.AI_COMMAND ?? file.aiCommand, 'claude');
|
|
33
|
+
const raw = getFilePlatformAiRaw(file, platform) ?? process.env.AI_COMMAND ?? file.aiCommand;
|
|
34
|
+
return normalizeAiCommand(raw, 'claude');
|
|
30
35
|
}
|
|
31
36
|
// Re-export file I/O and credential helpers from sub-modules
|
|
32
37
|
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';
|
|
@@ -197,42 +202,30 @@ export function loadConfig() {
|
|
|
197
202
|
const tcb = file.tools?.codebuddy ?? {};
|
|
198
203
|
const claudeProxy = process.env.CLAUDE_PROXY ?? tc.proxy;
|
|
199
204
|
const codexProxy = process.env.CODEX_PROXY ?? tcod.proxy;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
];
|
|
206
|
-
for (const p of npmPaths) {
|
|
207
|
-
try {
|
|
208
|
-
accessSync(p, constants.F_OK);
|
|
209
|
-
codexCliPath = p;
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
/* 尝试下一个路径 */
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
let codebuddyCliPath = process.env.CODEBUDDY_CLI_PATH ?? tcb.cliPath ?? 'codebuddy';
|
|
218
|
-
if (process.platform === 'win32' && codebuddyCliPath === 'codebuddy') {
|
|
205
|
+
// Windows 下若 CLI 路径仍是默认名,尝试在 npm 全局目录定位 .cmd。
|
|
206
|
+
// 默认可执行名从 registry 的 cliDefault 取(此前 opencode 遗漏了 Windows 解析)。
|
|
207
|
+
function resolveWindowsCliPath(cliDefault) {
|
|
208
|
+
if (process.platform !== 'win32')
|
|
209
|
+
return cliDefault;
|
|
219
210
|
const npmPaths = [
|
|
220
|
-
join(process.env.APPDATA || '', 'npm',
|
|
221
|
-
join(process.env.LOCALAPPDATA || '', 'npm',
|
|
211
|
+
join(process.env.APPDATA || '', 'npm', `${cliDefault}.cmd`),
|
|
212
|
+
join(process.env.LOCALAPPDATA || '', 'npm', `${cliDefault}.cmd`),
|
|
222
213
|
];
|
|
223
214
|
for (const p of npmPaths) {
|
|
224
215
|
try {
|
|
225
216
|
accessSync(p, constants.F_OK);
|
|
226
|
-
|
|
227
|
-
break;
|
|
217
|
+
return p;
|
|
228
218
|
}
|
|
229
219
|
catch {
|
|
230
220
|
/* 尝试下一个路径 */
|
|
231
221
|
}
|
|
232
222
|
}
|
|
223
|
+
return cliDefault;
|
|
233
224
|
}
|
|
225
|
+
const codexCliPath = process.env.CODEX_CLI_PATH ?? tcod.cliPath ?? resolveWindowsCliPath(AI_TOOL_BY_ID.codex.cliDefault ?? 'codex');
|
|
226
|
+
const codebuddyCliPath = process.env.CODEBUDDY_CLI_PATH ?? tcb.cliPath ?? resolveWindowsCliPath(AI_TOOL_BY_ID.codebuddy.cliDefault ?? 'codebuddy');
|
|
234
227
|
const topencode = file.tools?.opencode ?? {};
|
|
235
|
-
const opencodeCliPath = process.env.OPENCODE_CLI_PATH ?? topencode.cliPath ?? 'opencode';
|
|
228
|
+
const opencodeCliPath = process.env.OPENCODE_CLI_PATH ?? topencode.cliPath ?? resolveWindowsCliPath(AI_TOOL_BY_ID.opencode.cliDefault ?? 'opencode');
|
|
236
229
|
const claudeWorkDir = process.env.CLAUDE_WORK_DIR ?? tc.workDir ?? process.cwd();
|
|
237
230
|
const skipPermissions = process.env.OPEN_IM_SKIP_PERMISSIONS === 'false'
|
|
238
231
|
? false
|
|
@@ -563,7 +556,7 @@ export function getPlatformsWithCredentials(config) {
|
|
|
563
556
|
}
|
|
564
557
|
export function resolvePlatformAiCommand(config, platform) {
|
|
565
558
|
const v = config.platforms[platform]?.aiCommand;
|
|
566
|
-
return v
|
|
559
|
+
return isAiCommand(v) ? v : 'claude';
|
|
567
560
|
}
|
|
568
561
|
export function getConfiguredAiCommands(config) {
|
|
569
562
|
const commands = new Set();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
type
|
|
1
|
+
import type { AiCommand } from '../adapters/tool-registry.js';
|
|
2
|
+
type ToolId = AiCommand;
|
|
2
3
|
export declare function resolveWorkDirInput(baseDir: string, targetDir: string): string;
|
|
3
4
|
export declare class SessionManager {
|
|
4
5
|
private sessions;
|
|
@@ -4,6 +4,7 @@ import { randomBytes } from 'node:crypto';
|
|
|
4
4
|
import { dirname, join, resolve, win32 } from 'node:path';
|
|
5
5
|
import { createLogger } from '../logger.js';
|
|
6
6
|
import { APP_HOME } from '../constants.js';
|
|
7
|
+
import { AI_TOOL_IDS } from '../adapters/tool-registry.js';
|
|
7
8
|
const log = createLogger('Session');
|
|
8
9
|
const SESSIONS_FILE = join(APP_HOME, 'data', 'sessions.json');
|
|
9
10
|
export function resolveWorkDirInput(baseDir, targetDir) {
|
|
@@ -392,7 +393,7 @@ export class SessionManager {
|
|
|
392
393
|
}
|
|
393
394
|
}
|
|
394
395
|
clearConvSessionMappings(userId, convId) {
|
|
395
|
-
for (const toolId of
|
|
396
|
+
for (const toolId of AI_TOOL_IDS) {
|
|
396
397
|
this.convSessionMap.delete(this.getConvSessionKey(userId, convId, toolId));
|
|
397
398
|
}
|
|
398
399
|
}
|
package/dist/setup.js
CHANGED
|
@@ -10,6 +10,7 @@ import { join, dirname } from "node:path";
|
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { APP_HOME } from "./constants.js";
|
|
12
12
|
import { loadConfig, getPlatformsWithCredentials } from "./config.js";
|
|
13
|
+
import { normalizeAiCommand } from "./adapters/tool-registry.js";
|
|
13
14
|
function loadExistingConfig() {
|
|
14
15
|
const configPath = join(APP_HOME, "config.json");
|
|
15
16
|
if (!existsSync(configPath))
|
|
@@ -57,7 +58,7 @@ function getConfiguredPlatforms(existing) {
|
|
|
57
58
|
.map(({ label }) => label);
|
|
58
59
|
}
|
|
59
60
|
function defaultPlatformAi(v) {
|
|
60
|
-
return v
|
|
61
|
+
return normalizeAiCommand(v, "claude");
|
|
61
62
|
}
|
|
62
63
|
function question(prompt) {
|
|
63
64
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -952,6 +953,10 @@ export async function runInteractiveSetup() {
|
|
|
952
953
|
...baseTools.codebuddy,
|
|
953
954
|
cliPath: baseTools.codebuddy?.cliPath ?? "codebuddy",
|
|
954
955
|
},
|
|
956
|
+
opencode: {
|
|
957
|
+
...baseTools.opencode,
|
|
958
|
+
cliPath: baseTools.opencode?.cliPath ?? "opencode",
|
|
959
|
+
},
|
|
955
960
|
},
|
|
956
961
|
};
|
|
957
962
|
const outPlatforms = out.platforms;
|
package/dist/shared/utils.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AI_TOOLS } from '../adapters/tool-registry.js';
|
|
1
2
|
/** 消息头部品牌后缀,用于飞书等平台展示 */
|
|
2
3
|
export const OPEN_IM_BRAND_SUFFIX = ' · 通过 open-im 控制';
|
|
3
4
|
/** Default queue-full message. */
|
|
@@ -39,13 +40,14 @@ export function toReplyPlainText(value) {
|
|
|
39
40
|
export function escapePathForMarkdown(path) {
|
|
40
41
|
return `\`${path.replace(/`/g, '\\`')}\``;
|
|
41
42
|
}
|
|
42
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* AI 工具显示名称映射(aiCommand / adapter.toolId -> 用户友好名称)。
|
|
45
|
+
* 主体从 tool-registry 的 label 派生;额外补充 adapter toolId(如 claude-sdk)的别名。
|
|
46
|
+
*/
|
|
43
47
|
const AI_TOOL_DISPLAY_NAMES = {
|
|
44
|
-
|
|
45
|
-
/** ClaudeSDKAdapter.toolId
|
|
48
|
+
...Object.fromEntries(AI_TOOLS.map((t) => [t.id, t.label])),
|
|
49
|
+
/** ClaudeSDKAdapter.toolId,与 claude 相同展示名 */
|
|
46
50
|
'claude-sdk': 'Claude Code',
|
|
47
|
-
codex: 'Codex',
|
|
48
|
-
codebuddy: 'CodeBuddy',
|
|
49
51
|
};
|
|
50
52
|
/** 获取 AI 工具的显示名称 */
|
|
51
53
|
export function getAIToolDisplayName(aiCommand) {
|
package/package.json
CHANGED