@mingxy/ocosay 1.1.24 → 1.1.25
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/core/dependency-detector.d.ts +26 -0
- package/dist/core/dependency-detector.js +52 -0
- package/dist/core/dependency-installer.d.ts +15 -0
- package/dist/core/dependency-installer.js +175 -0
- package/dist/core/dependency-mapper.d.ts +59 -0
- package/dist/core/dependency-mapper.js +246 -0
- package/dist/package.json +1 -1
- package/dist/plugin.js +487 -68
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency-detector.ts - 检测层
|
|
3
|
+
* 捕获编译命令的 stderr 输出,解析缺失的头文件
|
|
4
|
+
*/
|
|
5
|
+
export interface DetectResult {
|
|
6
|
+
missingHeaders: string[];
|
|
7
|
+
rawOutput: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 从编译错误输出中解析缺失的头文件路径
|
|
11
|
+
* 匹配模式: "fatal error: alsa/asoundlib.h: No such file or directory"
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseMissingHeaders(errorOutput: string): string[];
|
|
14
|
+
/**
|
|
15
|
+
* 执行命令并捕获输出,不抛错
|
|
16
|
+
*/
|
|
17
|
+
export declare function execCapture(cmd: string, cwd?: string): {
|
|
18
|
+
success: boolean;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* 检测命令执行后的缺失依赖
|
|
24
|
+
*/
|
|
25
|
+
export declare function detectMissingDependencies(cmd: string, cwd?: string): DetectResult;
|
|
26
|
+
//# sourceMappingURL=dependency-detector.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency-detector.ts - 检测层
|
|
3
|
+
* 捕获编译命令的 stderr 输出,解析缺失的头文件
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
/**
|
|
7
|
+
* 从编译错误输出中解析缺失的头文件路径
|
|
8
|
+
* 匹配模式: "fatal error: alsa/asoundlib.h: No such file or directory"
|
|
9
|
+
*/
|
|
10
|
+
export function parseMissingHeaders(errorOutput) {
|
|
11
|
+
const pattern = /fatal error:\s+([^:]+):\s+No such file or directory/g;
|
|
12
|
+
const headers = [];
|
|
13
|
+
let match = pattern.exec(errorOutput);
|
|
14
|
+
while (match !== null) {
|
|
15
|
+
headers.push(match[1]);
|
|
16
|
+
match = pattern.exec(errorOutput);
|
|
17
|
+
}
|
|
18
|
+
return headers;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 执行命令并捕获输出,不抛错
|
|
22
|
+
*/
|
|
23
|
+
export function execCapture(cmd, cwd) {
|
|
24
|
+
try {
|
|
25
|
+
const stdout = execSync(cmd, {
|
|
26
|
+
cwd,
|
|
27
|
+
encoding: 'utf8',
|
|
28
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
29
|
+
});
|
|
30
|
+
return { success: true, stdout, stderr: '' };
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const error = err;
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
stdout: error.stdout ? error.stdout.toString() : '',
|
|
37
|
+
stderr: error.stderr ? error.stderr.toString() : '',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 检测命令执行后的缺失依赖
|
|
43
|
+
*/
|
|
44
|
+
export function detectMissingDependencies(cmd, cwd) {
|
|
45
|
+
const result = execCapture(cmd, cwd);
|
|
46
|
+
const missingHeaders = parseMissingHeaders(result.stderr);
|
|
47
|
+
return {
|
|
48
|
+
missingHeaders,
|
|
49
|
+
rawOutput: result.stderr,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=dependency-detector.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency-installer.ts - 安装层模块
|
|
3
|
+
* 根据平台调用合适的包管理器安装系统依赖
|
|
4
|
+
*/
|
|
5
|
+
import { notificationService } from './notification.js';
|
|
6
|
+
export interface InstallResult {
|
|
7
|
+
success: boolean;
|
|
8
|
+
installedPackages: string[];
|
|
9
|
+
failedPackages: string[];
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function installSystemPackages(packages: string[], notifSvc?: typeof notificationService): Promise<InstallResult>;
|
|
13
|
+
export declare function verifyInstallation(packages: string[]): Promise<boolean>;
|
|
14
|
+
export declare function installPackage(packageName: string, notifSvc?: typeof notificationService): Promise<InstallResult>;
|
|
15
|
+
//# sourceMappingURL=dependency-installer.d.ts.map
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency-installer.ts - 安装层模块
|
|
3
|
+
* 根据平台调用合适的包管理器安装系统依赖
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { createModuleLogger } from '../utils/logger.js';
|
|
7
|
+
import { detectPlatform } from './dependency-mapper.js';
|
|
8
|
+
import { execCapture } from './dependency-detector.js';
|
|
9
|
+
const logger = createModuleLogger('DependencyInstaller');
|
|
10
|
+
function getInstallCommandPrefix() {
|
|
11
|
+
const { platform, isWsl } = detectPlatform();
|
|
12
|
+
if (platform === 'linux' || isWsl) {
|
|
13
|
+
return 'sudo apt-get install -y';
|
|
14
|
+
}
|
|
15
|
+
else if (platform === 'darwin') {
|
|
16
|
+
return 'brew install';
|
|
17
|
+
}
|
|
18
|
+
return 'choco install -y';
|
|
19
|
+
}
|
|
20
|
+
function getCheckCommand(packageName) {
|
|
21
|
+
const { platform, isWsl } = detectPlatform();
|
|
22
|
+
if (platform === 'linux' || isWsl) {
|
|
23
|
+
return `dpkg -s ${packageName} 2>/dev/null | grep -q "Status: install ok installed"`;
|
|
24
|
+
}
|
|
25
|
+
else if (platform === 'darwin') {
|
|
26
|
+
return `brew list ${packageName} &>/dev/null`;
|
|
27
|
+
}
|
|
28
|
+
return `choco list --local-only ${packageName} &>/dev/null`;
|
|
29
|
+
}
|
|
30
|
+
function silentLog(level, message, extra) {
|
|
31
|
+
switch (level) {
|
|
32
|
+
case 'info':
|
|
33
|
+
logger.info(extra || {}, message);
|
|
34
|
+
break;
|
|
35
|
+
case 'warn':
|
|
36
|
+
logger.warn(extra || {}, message);
|
|
37
|
+
break;
|
|
38
|
+
case 'error':
|
|
39
|
+
logger.error(extra || {}, message);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function canSudoWithoutPassword() {
|
|
44
|
+
try {
|
|
45
|
+
execSync('sudo -n true 2>&1', { stdio: 'pipe' });
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function installSystemPackages(packages, notifSvc) {
|
|
53
|
+
const result = {
|
|
54
|
+
success: false,
|
|
55
|
+
installedPackages: [],
|
|
56
|
+
failedPackages: [],
|
|
57
|
+
};
|
|
58
|
+
const validPackages = packages.filter(p => p && p.length > 0 && p !== '臺');
|
|
59
|
+
if (validPackages.length === 0) {
|
|
60
|
+
silentLog('info', '没有需要安装的包');
|
|
61
|
+
return { ...result, success: true };
|
|
62
|
+
}
|
|
63
|
+
silentLog('info', `开始安装系统依赖: ${validPackages.join(', ')}`, { packages: validPackages });
|
|
64
|
+
notifSvc?.info('正在安装系统依赖...', 'Ocosay', 3000);
|
|
65
|
+
const installCommand = getInstallCommandPrefix();
|
|
66
|
+
const fullCommand = `${installCommand} ${validPackages.join(' ')}`;
|
|
67
|
+
silentLog('info', `执行安装命令: ${fullCommand}`);
|
|
68
|
+
// 安装前检测 sudo 免密权限
|
|
69
|
+
if (!canSudoWithoutPassword()) {
|
|
70
|
+
const msg = '需要 sudo 权限,请确保已配置 NOPASSWD';
|
|
71
|
+
result.error = msg;
|
|
72
|
+
notifSvc?.error('需要 sudo 权限', '请在终端执行: sudo visudo 添加 NOPASSWD 配置,配置好后请重启 OpenCode', 10000);
|
|
73
|
+
silentLog('error', msg);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const { platform, isWsl } = detectPlatform();
|
|
78
|
+
if (platform === 'linux' || isWsl) {
|
|
79
|
+
notifSvc?.info('正在更新包列表...', 'Ocosay', 3000);
|
|
80
|
+
silentLog('info', '更新 apt 包列表');
|
|
81
|
+
try {
|
|
82
|
+
execSync('sudo apt-get update', {
|
|
83
|
+
timeout: 120000,
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
});
|
|
86
|
+
silentLog('info', 'apt-get update 完成');
|
|
87
|
+
}
|
|
88
|
+
catch (updateErr) {
|
|
89
|
+
silentLog('warn', 'apt-get update 失败,继续尝试安装', { error: String(updateErr) });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
notifSvc?.info(`正在安装 ${validPackages.length} 个包...`, 'Ocosay', 5000);
|
|
93
|
+
const output = execSync(fullCommand, {
|
|
94
|
+
timeout: 300000,
|
|
95
|
+
encoding: 'utf8',
|
|
96
|
+
});
|
|
97
|
+
silentLog('info', `安装输出: ${output.substring(0, 500)}`);
|
|
98
|
+
const verifiedPackages = [];
|
|
99
|
+
const failedPackages = [];
|
|
100
|
+
for (const pkg of validPackages) {
|
|
101
|
+
const isInstalled = await verifyInstallation([pkg]);
|
|
102
|
+
if (isInstalled) {
|
|
103
|
+
verifiedPackages.push(pkg);
|
|
104
|
+
silentLog('info', `包 ${pkg} 安装验证成功`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
failedPackages.push(pkg);
|
|
108
|
+
silentLog('warn', `包 ${pkg} 安装验证失败`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
result.installedPackages = verifiedPackages;
|
|
112
|
+
result.failedPackages = failedPackages;
|
|
113
|
+
result.success = failedPackages.length === 0;
|
|
114
|
+
if (result.success) {
|
|
115
|
+
notifSvc?.success('系统依赖安装成功', verifiedPackages.join(', '), 5000);
|
|
116
|
+
silentLog('info', `所有包安装成功: ${verifiedPackages.join(', ')}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const errorMsg = failedPackages.length > 0
|
|
120
|
+
? `以下包安装失败: ${failedPackages.join(', ')}`
|
|
121
|
+
: '部分包安装失败';
|
|
122
|
+
result.error = errorMsg;
|
|
123
|
+
notifSvc?.warning('部分依赖安装失败', errorMsg, 8000);
|
|
124
|
+
silentLog('warn', errorMsg);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
129
|
+
silentLog('error', `安装命令执行失败: ${errorMessage}`, { error: errorMessage });
|
|
130
|
+
if (errorMessage.includes('sudo') || errorMessage.toLowerCase().includes('password')) {
|
|
131
|
+
result.error = '需要 sudo 权限,请确保已配置 NOPASSWD';
|
|
132
|
+
notifSvc?.error('需要 sudo 权限', '请在终端执行: sudo visudo', 10000);
|
|
133
|
+
}
|
|
134
|
+
else if (errorMessage.includes('already') || errorMessage.includes('is already')) {
|
|
135
|
+
result.success = true;
|
|
136
|
+
result.installedPackages = validPackages;
|
|
137
|
+
notifSvc?.success('依赖已存在', '无需重新安装', 3000);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
result.error = errorMessage;
|
|
141
|
+
notifSvc?.error('依赖安装失败', errorMessage.substring(0, 200), 8000);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
export async function verifyInstallation(packages) {
|
|
147
|
+
if (packages.length === 0) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
const validPackages = packages.filter(p => p && p.length > 0 && p !== '臺');
|
|
151
|
+
if (validPackages.length === 0) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
silentLog('info', `验证包安装状态: ${validPackages.join(', ')}`);
|
|
155
|
+
try {
|
|
156
|
+
for (const pkg of validPackages) {
|
|
157
|
+
const checkCmd = getCheckCommand(pkg);
|
|
158
|
+
const { success } = execCapture(checkCmd);
|
|
159
|
+
if (!success) {
|
|
160
|
+
silentLog('warn', `包 ${pkg} 验证失败`);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
silentLog('info', `所有包验证通过: ${validPackages.join(', ')}`);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
silentLog('error', `验证过程异常: ${String(err)}`);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export async function installPackage(packageName, notifSvc) {
|
|
173
|
+
return installSystemPackages([packageName], notifSvc);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=dependency-installer.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency-mapper.ts - 解析层模块
|
|
3
|
+
* 建立头文件 → 系统包名 的映射,支持多平台
|
|
4
|
+
*/
|
|
5
|
+
export interface HeaderMapping {
|
|
6
|
+
header: string;
|
|
7
|
+
package: string;
|
|
8
|
+
packageMac?: string;
|
|
9
|
+
packageWin?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PlatformInfo {
|
|
12
|
+
platform: 'linux' | 'darwin' | 'win32';
|
|
13
|
+
isWsl: boolean;
|
|
14
|
+
packageManager: 'apt-get' | 'brew' | 'choco';
|
|
15
|
+
installCommand: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PlatformInfo {
|
|
18
|
+
platform: 'linux' | 'darwin' | 'win32';
|
|
19
|
+
isWsl: boolean;
|
|
20
|
+
packageManager: 'apt-get' | 'brew' | 'choco';
|
|
21
|
+
installCommand: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const HEADER_TO_PACKAGE: HeaderMapping[];
|
|
24
|
+
/**
|
|
25
|
+
* 检测当前平台信息
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectPlatform(): PlatformInfo;
|
|
28
|
+
/**
|
|
29
|
+
* 将头文件映射为对应平台的系统包名
|
|
30
|
+
* @param header 头文件路径,如 "alsa/asoundlib.h"
|
|
31
|
+
* @param platform 平台标识,如 "linux", "darwin", "win32"
|
|
32
|
+
* @returns 包名数组,如果无法映射则返回空数组
|
|
33
|
+
*/
|
|
34
|
+
export declare function mapHeaderToPackages(header: string, platform: string): string[];
|
|
35
|
+
/**
|
|
36
|
+
* 批量将头文件映射为对应平台的系统包名
|
|
37
|
+
* @param headers 头文件路径数组
|
|
38
|
+
* @param platform 平台标识
|
|
39
|
+
* @returns 去重后的包名数组
|
|
40
|
+
*/
|
|
41
|
+
export declare function mapHeadersToPackages(headers: string[], platform: string): string[];
|
|
42
|
+
/**
|
|
43
|
+
* 生成安装命令
|
|
44
|
+
* @param packages 包名数组
|
|
45
|
+
* @param platformInfo 平台信息
|
|
46
|
+
* @returns 完整的安装命令
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateInstallCommand(packages: string[], platformInfo?: PlatformInfo): string;
|
|
49
|
+
export interface MappingDebugInfo {
|
|
50
|
+
header: string;
|
|
51
|
+
mappedPackages: string[];
|
|
52
|
+
platform: PlatformInfo;
|
|
53
|
+
rawMapping?: HeaderMapping;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 调试用:获取头文件的完整映射信息
|
|
57
|
+
*/
|
|
58
|
+
export declare function debugHeaderMapping(header: string): MappingDebugInfo;
|
|
59
|
+
//# sourceMappingURL=dependency-mapper.d.ts.map
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency-mapper.ts - 解析层模块
|
|
3
|
+
* 建立头文件 → 系统包名 的映射,支持多平台
|
|
4
|
+
*/
|
|
5
|
+
import { createRequire } from 'module';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
// ============================================================
|
|
8
|
+
// 映射表
|
|
9
|
+
// ============================================================
|
|
10
|
+
export const HEADER_TO_PACKAGE = [
|
|
11
|
+
// ALSA - Linux音频库
|
|
12
|
+
{
|
|
13
|
+
header: 'alsa/asoundlib.h',
|
|
14
|
+
package: 'libasound2-dev',
|
|
15
|
+
},
|
|
16
|
+
// PortAudio - 跨平台音频I/O库
|
|
17
|
+
{
|
|
18
|
+
header: 'portaudio.h',
|
|
19
|
+
package: 'libportaudio-dev',
|
|
20
|
+
packageMac: 'portaudio',
|
|
21
|
+
packageWin: 'portaudio',
|
|
22
|
+
},
|
|
23
|
+
// FFmpeg相关
|
|
24
|
+
{
|
|
25
|
+
header: 'libavcodec/avcodec.h',
|
|
26
|
+
package: 'libavcodec-dev',
|
|
27
|
+
packageMac: 'ffmpeg',
|
|
28
|
+
packageWin: 'ffmpeg',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
header: 'libavformat/avformat.h',
|
|
32
|
+
package: 'libavformat-dev',
|
|
33
|
+
packageMac: 'ffmpeg',
|
|
34
|
+
packageWin: 'ffmpeg',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
header: 'libavutil/avutil.h',
|
|
38
|
+
package: 'libavutil-dev',
|
|
39
|
+
packageMac: 'ffmpeg',
|
|
40
|
+
packageWin: 'ffmpeg',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
header: 'libswresample/swresample.h',
|
|
44
|
+
package: 'libswresample-dev',
|
|
45
|
+
packageMac: 'ffmpeg',
|
|
46
|
+
packageWin: 'ffmpeg',
|
|
47
|
+
},
|
|
48
|
+
// OpenAL - 3D音频API
|
|
49
|
+
{
|
|
50
|
+
header: 'AL/al.h',
|
|
51
|
+
package: 'libopenal-dev',
|
|
52
|
+
packageMac: 'openal',
|
|
53
|
+
packageWin: 'openal',
|
|
54
|
+
},
|
|
55
|
+
// SDL - 多媒体库
|
|
56
|
+
{
|
|
57
|
+
header: 'SDL2/SDL.h',
|
|
58
|
+
package: 'libsdl2-dev',
|
|
59
|
+
packageMac: 'sdl2',
|
|
60
|
+
packageWin: 'sdl2',
|
|
61
|
+
},
|
|
62
|
+
// PulseAudio - Linux音频服务
|
|
63
|
+
{
|
|
64
|
+
header: 'pulse/pulseaudio.h',
|
|
65
|
+
package: 'libpulse-dev',
|
|
66
|
+
},
|
|
67
|
+
// CoreAudio - macOS音频框架 (无头文件,纯框架)
|
|
68
|
+
{
|
|
69
|
+
header: 'CoreAudio/CoreAudio.h',
|
|
70
|
+
package: '臺', // macOS系统框架,无需安装包
|
|
71
|
+
packageMac: '',
|
|
72
|
+
},
|
|
73
|
+
// Windows特定
|
|
74
|
+
{
|
|
75
|
+
header: 'windows.h',
|
|
76
|
+
package: '', // Linux上不存在
|
|
77
|
+
packageWin: '', // Windows SDK自带
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
// ============================================================
|
|
81
|
+
// 平台检测
|
|
82
|
+
// ============================================================
|
|
83
|
+
/**
|
|
84
|
+
* 检测当前平台信息
|
|
85
|
+
*/
|
|
86
|
+
export function detectPlatform() {
|
|
87
|
+
const platform = process.platform;
|
|
88
|
+
const isWsl = detectWsl();
|
|
89
|
+
let packageManager;
|
|
90
|
+
let installCommand;
|
|
91
|
+
if (platform === 'linux' || isWsl) {
|
|
92
|
+
packageManager = 'apt-get';
|
|
93
|
+
installCommand = 'sudo apt-get install -y';
|
|
94
|
+
}
|
|
95
|
+
else if (platform === 'darwin') {
|
|
96
|
+
packageManager = 'brew';
|
|
97
|
+
installCommand = 'brew install';
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
packageManager = 'choco';
|
|
101
|
+
installCommand = 'choco install -y';
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
platform,
|
|
105
|
+
isWsl,
|
|
106
|
+
packageManager,
|
|
107
|
+
installCommand,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 检测是否运行在 WSL 环境中
|
|
112
|
+
*/
|
|
113
|
+
function detectWsl() {
|
|
114
|
+
if (process.platform !== 'linux')
|
|
115
|
+
return false;
|
|
116
|
+
try {
|
|
117
|
+
return require('fs').readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ============================================================
|
|
124
|
+
// 映射函数
|
|
125
|
+
// ============================================================
|
|
126
|
+
/**
|
|
127
|
+
* 根据平台获取包名
|
|
128
|
+
*/
|
|
129
|
+
function getPackageForPlatform(mapping, platform, isWsl) {
|
|
130
|
+
// Windows
|
|
131
|
+
if (platform === 'win32') {
|
|
132
|
+
// Windows下WSL环境使用Linux包管理器
|
|
133
|
+
if (isWsl) {
|
|
134
|
+
return mapping.package || null;
|
|
135
|
+
}
|
|
136
|
+
return mapping.packageWin || mapping.package || null;
|
|
137
|
+
}
|
|
138
|
+
// macOS
|
|
139
|
+
if (platform === 'darwin') {
|
|
140
|
+
// macOS系统框架头文件无需安装包
|
|
141
|
+
if (mapping.packageMac === '')
|
|
142
|
+
return null;
|
|
143
|
+
return mapping.packageMac || mapping.package || null;
|
|
144
|
+
}
|
|
145
|
+
// Linux (包括WSL)
|
|
146
|
+
// Linux系统框架头文件无需安装包
|
|
147
|
+
if (mapping.package === '')
|
|
148
|
+
return null;
|
|
149
|
+
return mapping.package || null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 将头文件映射为对应平台的系统包名
|
|
153
|
+
* @param header 头文件路径,如 "alsa/asoundlib.h"
|
|
154
|
+
* @param platform 平台标识,如 "linux", "darwin", "win32"
|
|
155
|
+
* @returns 包名数组,如果无法映射则返回空数组
|
|
156
|
+
*/
|
|
157
|
+
export function mapHeaderToPackages(header, platform) {
|
|
158
|
+
// 精确匹配
|
|
159
|
+
const exactMatch = HEADER_TO_PACKAGE.find(m => m.header.toLowerCase() === header.toLowerCase());
|
|
160
|
+
if (exactMatch) {
|
|
161
|
+
const pkg = getPackageForPlatform(exactMatch, platform, detectWsl());
|
|
162
|
+
if (pkg && pkg.length > 0 && pkg !== '臺') { // '臺' 是macOS CoreAudio的占位符,表示无需安装
|
|
163
|
+
return [pkg];
|
|
164
|
+
}
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
// 模糊匹配(仅匹配头文件 basename)
|
|
168
|
+
const headerBasename = header.split('/').pop()?.toLowerCase() || '';
|
|
169
|
+
const fuzzyMatch = HEADER_TO_PACKAGE.find(m => {
|
|
170
|
+
const mappingBasename = m.header.split('/').pop()?.toLowerCase() || '';
|
|
171
|
+
return mappingBasename === headerBasename;
|
|
172
|
+
});
|
|
173
|
+
if (fuzzyMatch) {
|
|
174
|
+
const pkg = getPackageForPlatform(fuzzyMatch, platform, detectWsl());
|
|
175
|
+
if (pkg && pkg.length > 0 && pkg !== '臺') {
|
|
176
|
+
return [pkg];
|
|
177
|
+
}
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 批量将头文件映射为对应平台的系统包名
|
|
184
|
+
* @param headers 头文件路径数组
|
|
185
|
+
* @param platform 平台标识
|
|
186
|
+
* @returns 去重后的包名数组
|
|
187
|
+
*/
|
|
188
|
+
export function mapHeadersToPackages(headers, platform) {
|
|
189
|
+
const packageSet = new Set();
|
|
190
|
+
for (const header of headers) {
|
|
191
|
+
const packages = mapHeaderToPackages(header, platform);
|
|
192
|
+
for (const pkg of packages) {
|
|
193
|
+
if (pkg.length > 0) {
|
|
194
|
+
packageSet.add(pkg);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return Array.from(packageSet);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 生成安装命令
|
|
202
|
+
* @param packages 包名数组
|
|
203
|
+
* @param platformInfo 平台信息
|
|
204
|
+
* @returns 完整的安装命令
|
|
205
|
+
*/
|
|
206
|
+
export function generateInstallCommand(packages, platformInfo) {
|
|
207
|
+
const info = platformInfo || detectPlatform();
|
|
208
|
+
if (packages.length === 0) {
|
|
209
|
+
return '';
|
|
210
|
+
}
|
|
211
|
+
if (info.platform === 'linux' || info.isWsl) {
|
|
212
|
+
return `sudo apt-get update && sudo apt-get install -y ${packages.join(' ')}`;
|
|
213
|
+
}
|
|
214
|
+
else if (info.platform === 'darwin') {
|
|
215
|
+
return `brew install ${packages.join(' ')}`;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
return `choco install -y ${packages.join(' ')}`;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 调试用:获取头文件的完整映射信息
|
|
223
|
+
*/
|
|
224
|
+
export function debugHeaderMapping(header) {
|
|
225
|
+
const platform = detectPlatform();
|
|
226
|
+
const exactMatch = HEADER_TO_PACKAGE.find(m => m.header.toLowerCase() === header.toLowerCase());
|
|
227
|
+
const headerBasename = header.split('/').pop()?.toLowerCase() || '';
|
|
228
|
+
const fuzzyMatch = !exactMatch
|
|
229
|
+
? HEADER_TO_PACKAGE.find(m => {
|
|
230
|
+
const mappingBasename = m.header.split('/').pop()?.toLowerCase() || '';
|
|
231
|
+
return mappingBasename === headerBasename;
|
|
232
|
+
})
|
|
233
|
+
: undefined;
|
|
234
|
+
const rawMapping = exactMatch || fuzzyMatch;
|
|
235
|
+
const pkg = rawMapping
|
|
236
|
+
? getPackageForPlatform(rawMapping, platform.platform, platform.isWsl)
|
|
237
|
+
: null;
|
|
238
|
+
const packages = (pkg && pkg.length > 0 && pkg !== '臺') ? [pkg] : [];
|
|
239
|
+
return {
|
|
240
|
+
header,
|
|
241
|
+
mappedPackages: packages,
|
|
242
|
+
platform,
|
|
243
|
+
rawMapping,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=dependency-mapper.js.map
|