-
Notifications
You must be signed in to change notification settings - Fork 285
Expand file tree
/
Copy pathpluginManager.js
More file actions
154 lines (135 loc) · 5.36 KB
/
pluginManager.js
File metadata and controls
154 lines (135 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import fs from "fs";
import path from "path";
import {spawn} from "child_process";
import {fileURLToPath, pathToFileURL} from "url";
// 获取 pluginManager.js 的目录
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// plugin.js 和 plugin.example.js 在上级目录
const userConfigPath = path.join(__dirname, "../plugin.js");
const exampleConfigPath = path.join(__dirname, "../plugin.example.js");
// 尝试加载用户配置,如果没有就用 example
let plugins = [];
try {
console.log(`检查插件配置文件: ${userConfigPath} 是否存在`);
if (fs.existsSync(userConfigPath)) {
plugins = (await import(pathToFileURL(userConfigPath).href)).default;
console.log("[pluginManager] 使用用户 plugin.js 配置");
} else if (fs.existsSync(exampleConfigPath)) {
plugins = (await import(pathToFileURL(exampleConfigPath).href)).default;
console.log("[pluginManager] 使用默认 plugin.example.js 配置");
}
} catch (err) {
console.error("[pluginManager] 加载插件配置失败:", err);
plugins = [];
}
/**
* 获取插件对应的二进制文件路径
* @param {string} rootDir 项目根目录
* @param {string} pluginPath 插件目录路径 (例: plugins/req-proxy)
* @param {string} pluginName 插件名 (例: req-proxy)
*/
function getPluginBinary(rootDir, pluginPath, pluginName) {
const platform = process.platform;
const binDir = path.join(rootDir, pluginPath);
let binaryName = null;
if (platform === "win32") {
binaryName = `${pluginName}-windows.exe`;
} else if (platform === "linux") {
binaryName = `${pluginName}-linux`;
} else if (platform === "darwin") {
binaryName = `${pluginName}-darwin`;
} else if (platform === "android") {
binaryName = `${pluginName}-android`;
} else {
console.log("[getPluginBinary] Unsupported platform: " + platform);
return null;
}
return path.join(binDir, binaryName);
}
function ensureExecutable(filePath) {
if (process.platform === "win32") {
// Windows 不需要 chmod,直接返回
return;
}
try {
const stats = fs.statSync(filePath);
if (!(stats.mode & 0o111)) {
fs.chmodSync(filePath, 0o755);
console.log(`[pluginManager] 已为插件 ${filePath} 添加执行权限`);
}
} catch (err) {
console.error(`[pluginManager] 无法设置执行权限: ${filePath}`, err.message);
}
}
/**
* 启动插件
* @param {Object} plugin 插件配置
* @param {string} rootDir 项目根目录
*/
function startPlugin(plugin, rootDir) {
if (!plugin.active) {
console.log(`[pluginManager] 插件 ${plugin.name} 未激活,跳过`);
return null;
}
const binary = getPluginBinary(rootDir, plugin.path, plugin.name);
if (!binary || !fs.existsSync(binary)) {
console.error(`[pluginManager] 插件 ${plugin.name} 的二进制文件不存在: ${binary}`);
return null;
}
console.log(`[pluginManager] 启动插件 ${plugin.name}: ${binary} ${plugin.params || ""}`);
const args = plugin.params ? plugin.params.split(" ") : [];
let proc;
try {
ensureExecutable(binary);
// 用 pipe 方式,便于我们捕获插件日志
proc = spawn(binary, args, {stdio: ["ignore", "pipe", "pipe"]});
// 检查是否真的启动了
if (!proc || !proc.pid) {
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败 (无效的进程 PID)`);
return null;
}
proc.stdout.on("data", (data) => {
console.log(`[${plugin.name}]`, data.toString().trim());
});
proc.stderr.on("data", (data) => {
console.log(`[${plugin.name}-STD]`, data.toString().trim());
});
proc.on("error", (err) => {
if (err.code === "EACCES") {
console.error(`[pluginManager] 插件 ${plugin.name} 无法执行: 没有执行权限,请运行: chmod +x ${binary}`);
} else if (err.code === "ENOENT") {
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败: 找不到可执行文件 ${binary}`);
} else {
console.error(`[pluginManager] 插件 ${plugin.name} 运行中出错:`, err.message);
}
// 标记为“启动失败”,避免 exit 再重复打印
proc._failedToSpawn = true;
});
proc.on("exit", (code, signal) => {
if (proc._failedToSpawn) return; // 忽略 spawn 失败导致的 exit
console.log(`[pluginManager] 插件 ${plugin.name} 退出 (code=${code}, signal=${signal})`);
});
return proc;
} catch (err) {
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败 (spawn 出错):`, err.message);
return null;
}
}
/**
* 启动所有插件
* @param {string} rootDir 项目根目录
*/
export function startAllPlugins(rootDir = process.cwd()) {
console.log("[pluginManager] 准备启动所有插件...");
const processes = {};
for (const plugin of plugins) {
const proc = startPlugin(plugin, rootDir);
if (proc) {
processes[plugin.name] = proc;
} else {
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败,未加入到 processes`);
}
}
return processes;
}