@qqbrowser/qbot-claw-launcher 0.9.33 → 0.9.35
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/config.d.ts +0 -3
- package/dist/config.js +1 -23
- package/dist/index.d.ts +0 -1
- package/dist/index.js +6 -127
- package/dist/installer.d.ts +0 -8
- package/dist/installer.js +5 -335
- package/dist/quarantine.d.ts +0 -34
- package/dist/quarantine.js +1 -315
- package/dist/reportLog.d.ts +0 -24
- package/dist/reportLog.js +1 -262
- package/dist/server.d.ts +0 -1
- package/dist/server.js +6 -425
- package/dist/service.d.ts +0 -9
- package/dist/service.js +6 -197
- package/dist/state.d.ts +0 -7
- package/dist/state.js +1 -62
- package/dist/utils.d.ts +0 -19
- package/dist/utils.js +1 -101
- package/node_modules/fs-xattr/build/Makefile +1 -1
- package/node_modules/fs-xattr/build/Release/xattr.node +0 -0
- package/node_modules/fs-xattr/build/config.gypi +1 -0
- package/package.json +3 -2
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/installer.d.ts.map +0 -1
- package/dist/installer.js.map +0 -1
- package/dist/quarantine.d.ts.map +0 -1
- package/dist/quarantine.js.map +0 -1
- package/dist/reportLog.d.ts.map +0 -1
- package/dist/reportLog.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js.map +0 -1
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
package/dist/config.d.ts
CHANGED
|
@@ -6,10 +6,7 @@ export declare const NPM_RC: string;
|
|
|
6
6
|
export declare const NODE_BIN_DIR: string;
|
|
7
7
|
export declare const QBOT_CLAW_CONFIG_PATH: string;
|
|
8
8
|
export declare const NPM_PACKAGE_NAME = "@qqbrowser/openclaw-qbot";
|
|
9
|
-
/** npm 全局 node_modules 目录 */
|
|
10
9
|
export declare const NPM_GLOBAL_MODULES: string;
|
|
11
10
|
export declare const QBOTCLAW_BIN: string;
|
|
12
11
|
export declare const MCPORTER_BIN: string;
|
|
13
|
-
export declare const LOG_FILE: string;
|
|
14
12
|
export declare const STATE_FILE: string;
|
|
15
|
-
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
CHANGED
|
@@ -1,23 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.STATE_FILE = exports.LOG_FILE = exports.MCPORTER_BIN = exports.QBOTCLAW_BIN = exports.NPM_GLOBAL_MODULES = exports.NPM_PACKAGE_NAME = exports.QBOT_CLAW_CONFIG_PATH = exports.NODE_BIN_DIR = exports.NPM_RC = exports.NPM_BIN = exports.PYTHON_BIN = exports.NODE_BIN = exports.QBOT_CLAW_DIR = void 0;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const os_1 = __importDefault(require("os"));
|
|
9
|
-
exports.QBOT_CLAW_DIR = path_1.default.join(os_1.default.homedir(), '.openclaw-qbot');
|
|
10
|
-
exports.NODE_BIN = path_1.default.join(exports.QBOT_CLAW_DIR, 'node', 'bin', 'node');
|
|
11
|
-
exports.PYTHON_BIN = path_1.default.join(exports.QBOT_CLAW_DIR, 'python', 'bin', 'python3');
|
|
12
|
-
exports.NPM_BIN = path_1.default.join(exports.QBOT_CLAW_DIR, 'node', 'bin', 'npm');
|
|
13
|
-
exports.NPM_RC = path_1.default.join(exports.QBOT_CLAW_DIR, 'node', '.npmrc');
|
|
14
|
-
exports.NODE_BIN_DIR = path_1.default.dirname(exports.NODE_BIN);
|
|
15
|
-
exports.QBOT_CLAW_CONFIG_PATH = path_1.default.join(exports.QBOT_CLAW_DIR, '.openclaw-qbot.json');
|
|
16
|
-
exports.NPM_PACKAGE_NAME = '@qqbrowser/openclaw-qbot';
|
|
17
|
-
/** npm 全局 node_modules 目录 */
|
|
18
|
-
exports.NPM_GLOBAL_MODULES = path_1.default.join(exports.QBOT_CLAW_DIR, 'node', 'lib', 'node_modules');
|
|
19
|
-
exports.QBOTCLAW_BIN = path_1.default.join(exports.QBOT_CLAW_DIR, 'node', 'bin', 'qb-qbot-claw');
|
|
20
|
-
exports.MCPORTER_BIN = path_1.default.join(exports.QBOT_CLAW_DIR, 'node', 'bin', 'mcporter');
|
|
21
|
-
exports.LOG_FILE = path_1.default.join(exports.QBOT_CLAW_DIR, 'launcher.log');
|
|
22
|
-
exports.STATE_FILE = path_1.default.join(exports.QBOT_CLAW_DIR, 'launcher.json');
|
|
23
|
-
//# sourceMappingURL=config.js.map
|
|
1
|
+
"use strict";var n=exports&&exports.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.STATE_FILE=exports.MCPORTER_BIN=exports.QBOTCLAW_BIN=exports.NPM_GLOBAL_MODULES=exports.NPM_PACKAGE_NAME=exports.QBOT_CLAW_CONFIG_PATH=exports.NODE_BIN_DIR=exports.NPM_RC=exports.NPM_BIN=exports.PYTHON_BIN=exports.NODE_BIN=exports.QBOT_CLAW_DIR=void 0;const _=n(require("path")),o=n(require("os"));exports.QBOT_CLAW_DIR=_.default.join(o.default.homedir(),".openclaw-qbot"),exports.NODE_BIN=_.default.join(exports.QBOT_CLAW_DIR,"node","bin","node"),exports.PYTHON_BIN=_.default.join(exports.QBOT_CLAW_DIR,"python","bin","python3"),exports.NPM_BIN=_.default.join(exports.QBOT_CLAW_DIR,"node","bin","npm"),exports.NPM_RC=_.default.join(exports.QBOT_CLAW_DIR,"node",".npmrc"),exports.NODE_BIN_DIR=_.default.dirname(exports.NODE_BIN),exports.QBOT_CLAW_CONFIG_PATH=_.default.join(exports.QBOT_CLAW_DIR,".openclaw-qbot.json"),exports.NPM_PACKAGE_NAME="@qqbrowser/openclaw-qbot",exports.NPM_GLOBAL_MODULES=_.default.join(exports.QBOT_CLAW_DIR,"node","lib","node_modules"),exports.QBOTCLAW_BIN=_.default.join(exports.QBOT_CLAW_DIR,"node","bin","qb-qbot-claw"),exports.MCPORTER_BIN=_.default.join(exports.QBOT_CLAW_DIR,"node","bin","mcporter"),exports.STATE_FILE=_.default.join(exports.QBOT_CLAW_DIR,"launcher.json");
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,128 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const child_process_1 = require("child_process");
|
|
10
|
-
const config_1 = require("./config");
|
|
11
|
-
const installer_1 = require("./installer");
|
|
12
|
-
const service_1 = require("./service");
|
|
13
|
-
const reportLog_1 = require("./reportLog");
|
|
14
|
-
const EXPECTED_PARENT_EXEC = '/Applications/QQBrowser.app/Contents/MacOS/QQBrowser';
|
|
15
|
-
const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.resolve(__dirname, '..', 'package.json'), 'utf-8'));
|
|
16
|
-
/**
|
|
17
|
-
* 检查父进程可执行文件路径,仅允许 QQBrowser 启动
|
|
18
|
-
*/
|
|
19
|
-
function checkParentProcess() {
|
|
20
|
-
const ppidCheck = process.ppid;
|
|
21
|
-
let parentExecPath = 'unknown';
|
|
22
|
-
try {
|
|
23
|
-
parentExecPath = (0, child_process_1.execSync)(`ps -p ${ppidCheck} -o comm=`, { encoding: 'utf-8' }).trim();
|
|
24
|
-
}
|
|
25
|
-
catch (err) {
|
|
26
|
-
(0, reportLog_1.log)(`[launcher] 获取父进程可执行文件路径失败: ${err.message}`);
|
|
27
|
-
}
|
|
28
|
-
(0, reportLog_1.log)(`[launcher] 父进程 pid: ${ppidCheck}, 可执行文件路径: ${parentExecPath}`);
|
|
29
|
-
if (parentExecPath !== EXPECTED_PARENT_EXEC) {
|
|
30
|
-
(0, reportLog_1.log)(`[launcher] 父进程不是 QQBrowser,拒绝启动。期望: ${EXPECTED_PARENT_EXEC},实际: ${parentExecPath}`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 注册全局未捕获异常保护,防止进程崩溃
|
|
36
|
-
*/
|
|
37
|
-
function registerExceptionHandlers() {
|
|
38
|
-
process.on('uncaughtException', (err) => {
|
|
39
|
-
const msg = `[${new Date().toISOString()}] [uncaughtException] ${err.message}\n${err.stack || ''}\n`;
|
|
40
|
-
process.stderr.write(msg);
|
|
41
|
-
try {
|
|
42
|
-
fs_1.default.appendFileSync(config_1.LOG_FILE, msg);
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
}
|
|
46
|
-
if (installer_1.installState.status === 'downloading') {
|
|
47
|
-
installer_1.installState.status = 'failed';
|
|
48
|
-
installer_1.installState.message = `进程异常: ${err.message}`;
|
|
49
|
-
installer_1.installState.endTime = new Date().toISOString();
|
|
50
|
-
}
|
|
51
|
-
// 不退出进程,继续运行
|
|
52
|
-
});
|
|
53
|
-
process.on('unhandledRejection', (reason) => {
|
|
54
|
-
const msg = `[${new Date().toISOString()}] [unhandledRejection] ${reason}\n`;
|
|
55
|
-
process.stderr.write(msg);
|
|
56
|
-
try {
|
|
57
|
-
fs_1.default.appendFileSync(config_1.LOG_FILE, msg);
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
}
|
|
61
|
-
if (installer_1.installState.status === 'downloading') {
|
|
62
|
-
installer_1.installState.status = 'failed';
|
|
63
|
-
installer_1.installState.message = `未处理的 Promise 拒绝: ${reason}`;
|
|
64
|
-
installer_1.installState.endTime = new Date().toISOString();
|
|
65
|
-
}
|
|
66
|
-
// 不退出进程,继续运行
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* 显示帮助信息
|
|
71
|
-
*/
|
|
72
|
-
function showHelp() {
|
|
73
|
-
console.log('用法: claw-launcher <command>');
|
|
74
|
-
console.log('');
|
|
75
|
-
console.log('命令:');
|
|
76
|
-
console.log(' start 在当前进程启动服务');
|
|
77
|
-
console.log(' stop 停止服务');
|
|
78
|
-
console.log(' version 显示版本号');
|
|
79
|
-
console.log(' help 显示帮助信息');
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* 处理命令行参数并执行对应命令
|
|
83
|
-
*/
|
|
84
|
-
function handleCommand(args) {
|
|
85
|
-
const command = args[0];
|
|
86
|
-
// 不要在启动服务时往 console 中写日志!!
|
|
87
|
-
if (command === 'start') {
|
|
88
|
-
(0, reportLog_1.log)(`[launcher] command line args: ${JSON.stringify(args)}`);
|
|
89
|
-
const ppidArgIndex = args.indexOf('--ppid');
|
|
90
|
-
const specifiedPpid = ppidArgIndex !== -1 ? parseInt(args[ppidArgIndex + 1], 10) : NaN;
|
|
91
|
-
const ppid = !isNaN(specifiedPpid) ? specifiedPpid : undefined;
|
|
92
|
-
const clawVersionArgIndex = args.indexOf('--claw-version');
|
|
93
|
-
const clawVersion = clawVersionArgIndex !== -1 ? args[clawVersionArgIndex + 1] : undefined;
|
|
94
|
-
(0, reportLog_1.log)(`[launcher] start service, ppid: ${ppid}, clawVersion: ${clawVersion}`);
|
|
95
|
-
(0, service_1.startService)(ppid, clawVersion).catch(() => {
|
|
96
|
-
process.stdout.write(JSON.stringify({ state: 'err', pid: 0, port: 0 }) + '\n');
|
|
97
|
-
process.stdout.uncork();
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
else if (command === 'stop') {
|
|
101
|
-
(0, service_1.stopService)();
|
|
102
|
-
}
|
|
103
|
-
else if (command === 'version' || command === '--version' || command === '-v') {
|
|
104
|
-
process.stdout.write(pkg.version + '\n');
|
|
105
|
-
process.stdout.uncork();
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
showHelp();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* 主入口函数
|
|
113
|
-
*/
|
|
114
|
-
function main() {
|
|
115
|
-
// 初始化日志上报参数(读取环境变量 guid/qimei36、自身版本、QBotClaw 版本)
|
|
116
|
-
(0, reportLog_1.initLog)();
|
|
117
|
-
(0, reportLog_1.log)('[launcher] Galileo initialized.');
|
|
118
|
-
(0, reportLog_1.log)('[launcher] QBot Claw Launcher Start.');
|
|
119
|
-
// 检查父进程
|
|
120
|
-
checkParentProcess();
|
|
121
|
-
// 注册全局异常处理
|
|
122
|
-
registerExceptionHandlers();
|
|
123
|
-
// 处理命令行参数
|
|
124
|
-
const args = process.argv.slice(2);
|
|
125
|
-
handleCommand(args);
|
|
126
|
-
}
|
|
127
|
-
main();
|
|
128
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
"use strict";var l=exports&&exports.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const u=l(require("fs")),d=l(require("path")),g=require("child_process"),a=require("./service"),o=require("./reportLog"),p="/Applications/QQBrowser.app/Contents/MacOS/QQBrowser",f=JSON.parse(u.default.readFileSync(d.default.resolve(__dirname,"..","package.json"),"utf-8"));function h(){const e=process.ppid;let n="unknown";try{n=(0,g.execSync)(`ps -p ${e} -o comm=`,{encoding:"utf-8"}).trim()}catch(s){(0,o.log)(`[launcher] \u83B7\u53D6\u7236\u8FDB\u7A0B\u53EF\u6267\u884C\u6587\u4EF6\u8DEF\u5F84\u5931\u8D25: ${s.message}`)}(0,o.log)(`[launcher] \u7236\u8FDB\u7A0B pid: ${e}, \u53EF\u6267\u884C\u6587\u4EF6\u8DEF\u5F84: ${n}`),n!==p&&((0,o.log)(`[launcher] \u7236\u8FDB\u7A0B\u4E0D\u662F QQBrowser\uFF0C\u62D2\u7EDD\u542F\u52A8\u3002\u671F\u671B: ${p}\uFF0C\u5B9E\u9645: ${n}`),process.exit(1))}function _(){process.on("uncaughtException",e=>{(0,o.log)(`[uncaughtException] ${e.message}
|
|
3
|
+
${e.stack||""}
|
|
4
|
+
`)}),process.on("unhandledRejection",e=>{(0,o.log)(`[${new Date().toISOString()}] [unhandledRejection] ${e}
|
|
5
|
+
`)})}function m(){console.log("\u7528\u6CD5: claw-launcher <command>"),console.log(""),console.log("\u547D\u4EE4:"),console.log(" start \u5728\u5F53\u524D\u8FDB\u7A0B\u542F\u52A8\u670D\u52A1"),console.log(" stop \u505C\u6B62\u670D\u52A1"),console.log(" version \u663E\u793A\u7248\u672C\u53F7"),console.log(" help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F")}function v(e){const n=e[0];if(n==="start"){(0,o.log)(`[launcher] command line args: ${JSON.stringify(e)}`);const s=e.indexOf("--ppid"),t=s!==-1?parseInt(e[s+1],10):NaN,c=isNaN(t)?void 0:t,r=e.indexOf("--claw-version"),i=r!==-1?e[r+1]:void 0;(0,o.log)(`[launcher] start service, ppid: ${c}, clawVersion: ${i}`),(0,a.startService)(c,i).catch(()=>{process.stdout.write(JSON.stringify({state:"err",pid:0,port:0})+`
|
|
6
|
+
`),process.stdout.uncork()})}else n==="stop"?(0,a.stopService)():n==="version"||n==="--version"||n==="-v"?(process.stdout.write(f.version+`
|
|
7
|
+
`),process.stdout.uncork()):m()}function w(){(0,o.initLog)(),(0,o.log)("[launcher] Galileo initialized."),(0,o.log)("[launcher] QBot Claw Launcher Start."),h(),_();const e=process.argv.slice(2);v(e)}w();
|
package/dist/installer.d.ts
CHANGED
|
@@ -7,14 +7,6 @@ export interface InstallState {
|
|
|
7
7
|
}
|
|
8
8
|
export declare let installState: InstallState;
|
|
9
9
|
export declare const PYTHON_DIR: string;
|
|
10
|
-
/**
|
|
11
|
-
* 执行 QBotClaw 命令
|
|
12
|
-
*/
|
|
13
10
|
export declare function runQBotClaw(args: string[], res?: http.ServerResponse): Promise<void>;
|
|
14
|
-
/** 步骤 12:配置并启动 QBotClaw gateway */
|
|
15
11
|
export declare function configAndRunQBotClaw(): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* 执行安装流程(异步,日志实时写入 res)
|
|
18
|
-
*/
|
|
19
12
|
export declare function runInstall(res: http.ServerResponse, version: string): void;
|
|
20
|
-
//# sourceMappingURL=installer.d.ts.map
|
package/dist/installer.js
CHANGED
|
@@ -1,335 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
exports.PYTHON_DIR = exports.installState = void 0;
|
|
7
|
-
exports.runQBotClaw = runQBotClaw;
|
|
8
|
-
exports.configAndRunQBotClaw = configAndRunQBotClaw;
|
|
9
|
-
exports.runInstall = runInstall;
|
|
10
|
-
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const child_process_1 = require("child_process");
|
|
13
|
-
const config_1 = require("./config");
|
|
14
|
-
const utils_1 = require("./utils");
|
|
15
|
-
const state_1 = require("./state");
|
|
16
|
-
const quarantine_1 = require("./quarantine");
|
|
17
|
-
const reportLog_1 = require("./reportLog");
|
|
18
|
-
function sendProgress(res, totalSteps, currentStep, message) {
|
|
19
|
-
if (res && res.writable) {
|
|
20
|
-
const progress = totalSteps > 0 ? Math.round((currentStep / totalSteps) * 10000) / 100 : 0;
|
|
21
|
-
try {
|
|
22
|
-
res.write(JSON.stringify({ status: 'downloading', progress, totalSteps, currentStep, message }) + '\n');
|
|
23
|
-
}
|
|
24
|
-
catch { }
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
exports.installState = { status: 'idle', message: '未开始' };
|
|
28
|
-
exports.PYTHON_DIR = path_1.default.join(config_1.QBOT_CLAW_DIR, 'python');
|
|
29
|
-
/**
|
|
30
|
-
* 执行 QBotClaw 命令
|
|
31
|
-
*/
|
|
32
|
-
function runQBotClaw(args, res) {
|
|
33
|
-
return new Promise((resolve) => {
|
|
34
|
-
const child = (0, child_process_1.spawn)(config_1.QBOTCLAW_BIN, args, {
|
|
35
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
36
|
-
env: {
|
|
37
|
-
PATH: config_1.NODE_BIN_DIR,
|
|
38
|
-
HOME: process.env.HOME,
|
|
39
|
-
OPENCLAW_TELEMETRY_GALIELO_GUID: process.env.QB_GUID || '',
|
|
40
|
-
OPENCLAW_TELEMETRY_GALIELO_QIEMI36: process.env.QB_QIMEI36 || '',
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
(0, reportLog_1.log)(`[QBotClaw] 执行: QBotClaw ${args.join(' ')}`);
|
|
44
|
-
child.stdout.on('data', (chunk) => {
|
|
45
|
-
const text = chunk.toString();
|
|
46
|
-
process.stdout.write(`[QBotClaw] ${text}`);
|
|
47
|
-
if (res && res.writable) {
|
|
48
|
-
try {
|
|
49
|
-
res.write(text);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
child.stderr.on('data', (chunk) => {
|
|
56
|
-
const text = chunk.toString();
|
|
57
|
-
process.stderr.write(`[QBotClaw] ${text}`);
|
|
58
|
-
if (res && res.writable) {
|
|
59
|
-
try {
|
|
60
|
-
res.write(text);
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
child.on('close', (code) => {
|
|
67
|
-
(0, reportLog_1.log)(`[QBotClaw] 执行完成,退出码: ${code}`);
|
|
68
|
-
resolve();
|
|
69
|
-
});
|
|
70
|
-
child.on('error', (err) => {
|
|
71
|
-
(0, reportLog_1.log)(`[QBotClaw] 启动失败: ${err.message}`);
|
|
72
|
-
resolve();
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
/** 步骤 1:确保 ~/.qbot-claw 目录存在 */
|
|
77
|
-
function ensureClawDir() {
|
|
78
|
-
if (!fs_1.default.existsSync(config_1.QBOT_CLAW_DIR)) {
|
|
79
|
-
fs_1.default.mkdirSync(config_1.QBOT_CLAW_DIR, { recursive: true });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/** 写入 pip.conf 配置文件(不存在时写入) */
|
|
83
|
-
function ensurePipConf() {
|
|
84
|
-
const pipConfPath = path_1.default.join(exports.PYTHON_DIR, 'pip.conf');
|
|
85
|
-
if (fs_1.default.existsSync(pipConfPath)) {
|
|
86
|
-
(0, reportLog_1.log)(`pip.conf 已存在,跳过`);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const pipConf = [
|
|
90
|
-
'[global]',
|
|
91
|
-
'index-url = https://mirrors.tencent.com/pypi/simple/',
|
|
92
|
-
`cache-dir = ${path_1.default.join(exports.PYTHON_DIR, '.pip-cache')}`,
|
|
93
|
-
'',
|
|
94
|
-
'[install]',
|
|
95
|
-
`prefix = ${exports.PYTHON_DIR}`,
|
|
96
|
-
'',
|
|
97
|
-
].join('\n');
|
|
98
|
-
fs_1.default.writeFileSync(pipConfPath, pipConf, 'utf-8');
|
|
99
|
-
(0, reportLog_1.log)(`已写入 pip.conf: ${pipConfPath}`);
|
|
100
|
-
}
|
|
101
|
-
/** 全局安装 mcporter(已安装则跳过) */
|
|
102
|
-
async function installMcporter(res) {
|
|
103
|
-
if (fs_1.default.existsSync(config_1.MCPORTER_BIN)) {
|
|
104
|
-
(0, reportLog_1.log)(`mcporter 已安装,跳过`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
(0, reportLog_1.log)(`开始安装 mcporter...`);
|
|
108
|
-
await new Promise((resolve, reject) => {
|
|
109
|
-
const child = (0, child_process_1.spawn)(config_1.NPM_BIN, ['--userconfig', config_1.NPM_RC, 'install', '-g', 'mcporter'], {
|
|
110
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
111
|
-
env: { ...process.env, PATH: `${path_1.default.dirname(config_1.NPM_BIN)}:${process.env.PATH}` },
|
|
112
|
-
});
|
|
113
|
-
child.stdout.on('data', (chunk) => {
|
|
114
|
-
const msg = `[mcporter] ${chunk.toString().trim()}`;
|
|
115
|
-
(0, reportLog_1.log)(msg);
|
|
116
|
-
sendProgress(res, 0, 0, msg);
|
|
117
|
-
});
|
|
118
|
-
child.stderr.on('data', (chunk) => {
|
|
119
|
-
const msg = `[mcporter] ${chunk.toString().trim()}`;
|
|
120
|
-
(0, reportLog_1.log)(msg);
|
|
121
|
-
sendProgress(res, 0, 0, msg);
|
|
122
|
-
});
|
|
123
|
-
child.on('close', (code) => {
|
|
124
|
-
if (code === 0) {
|
|
125
|
-
(0, reportLog_1.log)(`mcporter 安装成功`);
|
|
126
|
-
resolve();
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
reject(new Error(`mcporter 安装失败,退出码: ${code}`));
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
child.on('error', reject);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
/** 执行 npm 命令的通用辅助函数 */
|
|
136
|
-
function runNpmCommand(args, label) {
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
const child = (0, child_process_1.spawn)(config_1.NPM_BIN, ['--userconfig', config_1.NPM_RC, ...args], {
|
|
139
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
140
|
-
env: { ...process.env, PATH: `${path_1.default.dirname(config_1.NPM_BIN)}:${process.env.PATH}` },
|
|
141
|
-
});
|
|
142
|
-
child.stdout.on('data', (chunk) => {
|
|
143
|
-
const msg = `[npm] ${chunk.toString().trim()}`;
|
|
144
|
-
(0, reportLog_1.log)(msg);
|
|
145
|
-
});
|
|
146
|
-
child.stderr.on('data', (chunk) => {
|
|
147
|
-
const msg = `[npm] ${chunk.toString().trim()}`;
|
|
148
|
-
(0, reportLog_1.log)(msg);
|
|
149
|
-
});
|
|
150
|
-
child.on('close', (code) => {
|
|
151
|
-
if (code === 0) {
|
|
152
|
-
(0, reportLog_1.log)(`${label} 成功`);
|
|
153
|
-
resolve();
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
reject(new Error(`${label} 失败,退出码: ${code}`));
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
child.on('error', reject);
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* 使用 npm install -g --ignore-scripts 下载解压 QBotClaw(不执行 postinstall 脚本)
|
|
164
|
-
*
|
|
165
|
-
* 为什么跳过脚本:
|
|
166
|
-
* npm install 过程中,FSEvents 可能丢弃事件导致 watcher 遗漏文件。
|
|
167
|
-
* 如果 postinstall 脚本(如 node-llama-cpp)在 install 过程中 dlopen 这些文件,
|
|
168
|
-
* 会被 macOS Gatekeeper 拦截。拆分步骤后,可以在 dlopen 之前 100% 确保
|
|
169
|
-
* 所有 native 二进制文件的隔离属性已被清除。
|
|
170
|
-
*/
|
|
171
|
-
async function npmDownloadQBotClaw(version) {
|
|
172
|
-
const packageSpec = `${config_1.NPM_PACKAGE_NAME}@${version}`;
|
|
173
|
-
(0, reportLog_1.log)(`开始安装 ${packageSpec}(--ignore-scripts,仅下载解压)...`);
|
|
174
|
-
await runNpmCommand(['install', '-g', '--ignore-scripts', packageSpec], `${packageSpec} 下载解压`);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* 执行 npm rebuild,触发所有 postinstall 脚本
|
|
178
|
-
*
|
|
179
|
-
* 调用前应确保已完成隔离属性清除,使所有 dylib/node 文件在 rebuild 时已安全。
|
|
180
|
-
*/
|
|
181
|
-
async function npmRebuildQBotClaw(version) {
|
|
182
|
-
const packageSpec = `${config_1.NPM_PACKAGE_NAME}@${version}`;
|
|
183
|
-
(0, reportLog_1.log)(`开始 npm rebuild(执行 postinstall 脚本)...`);
|
|
184
|
-
await runNpmCommand(['rebuild', '-g', config_1.NPM_PACKAGE_NAME], `${packageSpec} rebuild`);
|
|
185
|
-
(0, reportLog_1.log)(`${packageSpec} 安装成功`);
|
|
186
|
-
}
|
|
187
|
-
/** 读取 npm 全局安装目录中的 preset-config.json,修改 gateway.port,然后覆盖写入到 QBOT_CLAW_CONFIG_PATH */
|
|
188
|
-
async function copyPresetConfig(gatewayPort, mcpHttpPort, mcpWsPort) {
|
|
189
|
-
// 配置来源路径:优先使用现有配置文件,若不存在则从 npm 全局 node_modules 中读取
|
|
190
|
-
const npmPresetPath = path_1.default.join(config_1.NPM_GLOBAL_MODULES, config_1.NPM_PACKAGE_NAME, 'preset-config.json');
|
|
191
|
-
let configPath;
|
|
192
|
-
if (fs_1.default.existsSync(config_1.QBOT_CLAW_CONFIG_PATH)) {
|
|
193
|
-
configPath = config_1.QBOT_CLAW_CONFIG_PATH;
|
|
194
|
-
(0, reportLog_1.log)(`使用现有配置文件: ${configPath}`);
|
|
195
|
-
}
|
|
196
|
-
else if (fs_1.default.existsSync(npmPresetPath)) {
|
|
197
|
-
configPath = npmPresetPath;
|
|
198
|
-
(0, reportLog_1.log)(`现有配置文件不存在,使用 npm 全局安装目录中的配置: ${configPath}`);
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
(0, reportLog_1.log)(`配置文件不存在,跳过配置写入: ${config_1.QBOT_CLAW_CONFIG_PATH}`);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
// 读取配置
|
|
205
|
-
let config;
|
|
206
|
-
try {
|
|
207
|
-
config = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
208
|
-
(0, reportLog_1.log)(`已读取配置: ${configPath}`);
|
|
209
|
-
}
|
|
210
|
-
catch (err) {
|
|
211
|
-
(0, reportLog_1.log)(`读取配置失败: ${configPath},${err.message},跳过配置写入`);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
// 修改 gateway.port 并写入
|
|
215
|
-
if (gatewayPort !== undefined) {
|
|
216
|
-
config.gateway = { ...(config.gateway ?? {}), port: gatewayPort };
|
|
217
|
-
(0, reportLog_1.log)(`已将 gateway.port=${gatewayPort} 写入配置`);
|
|
218
|
-
}
|
|
219
|
-
// 修改 plugins.entries.x5use-browser-control.config 中的端口
|
|
220
|
-
config.plugins = config.plugins ?? {};
|
|
221
|
-
config.plugins.entries = config.plugins.entries ?? {};
|
|
222
|
-
config.plugins.entries['x5use-browser-control'] = config.plugins.entries['x5use-browser-control'] ?? {};
|
|
223
|
-
config.plugins.entries['x5use-browser-control'].config = {
|
|
224
|
-
...(config.plugins.entries['x5use-browser-control'].config ?? {}),
|
|
225
|
-
wsPort: mcpWsPort,
|
|
226
|
-
httpPort: mcpHttpPort,
|
|
227
|
-
};
|
|
228
|
-
(0, reportLog_1.log)(`已将 x5use-browser-control wsPort=${mcpWsPort}, httpPort=${mcpHttpPort} 写入配置`);
|
|
229
|
-
fs_1.default.writeFileSync(config_1.QBOT_CLAW_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
230
|
-
(0, reportLog_1.log)(`配置文件已写入: ${config_1.QBOT_CLAW_CONFIG_PATH}`);
|
|
231
|
-
}
|
|
232
|
-
/** 步骤 12:配置并启动 QBotClaw gateway */
|
|
233
|
-
async function configAndRunQBotClaw() {
|
|
234
|
-
// 如果已有 gateway 在某个端口运行(端口不可用),则复用原端口;否则寻找新端口
|
|
235
|
-
const state = (0, state_1.readState)();
|
|
236
|
-
let gatewayPort;
|
|
237
|
-
if (state.gatewayPort && !(await (0, state_1.isPortAvailable)(state.gatewayPort))) {
|
|
238
|
-
gatewayPort = state.gatewayPort;
|
|
239
|
-
(0, reportLog_1.log)(`检测到 QBotClaw gateway 已在端口 ${gatewayPort} 运行,复用原端口`);
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
gatewayPort = await (0, utils_1.findAvailablePort)();
|
|
243
|
-
}
|
|
244
|
-
(0, reportLog_1.log)(`后台启动 QBotClaw gateway run --port ${gatewayPort} --force`);
|
|
245
|
-
const mcpHttpPort = await (0, utils_1.findAvailablePort)();
|
|
246
|
-
const mcpWsPort = await (0, utils_1.findAvailablePort)();
|
|
247
|
-
// 读取 preset-config.json,修改 gateway.port,覆盖写入配置文件
|
|
248
|
-
await copyPresetConfig(gatewayPort, mcpHttpPort, mcpWsPort);
|
|
249
|
-
const child = (0, child_process_1.spawn)(config_1.QBOTCLAW_BIN, ['gateway', 'run', '--port', String(gatewayPort), '--force'], {
|
|
250
|
-
stdio: 'ignore',
|
|
251
|
-
env: {
|
|
252
|
-
PATH: config_1.NODE_BIN_DIR,
|
|
253
|
-
HOME: process.env.HOME,
|
|
254
|
-
OPENCLAW_TELEMETRY_GALIELO_GUID: process.env.QB_GUID || '',
|
|
255
|
-
OPENCLAW_TELEMETRY_GALIELO_QIEMI36: process.env.QB_QIMEI36 || '',
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
child.unref();
|
|
259
|
-
const gatewayPid = child.pid;
|
|
260
|
-
(0, reportLog_1.log)(`QBotClaw gateway 已在后台启动,PID: ${gatewayPid},端口: ${gatewayPort}`);
|
|
261
|
-
// 将 gateway 进程信息写入 launcher.json
|
|
262
|
-
(0, state_1.writeState)({ ...(0, state_1.readState)(), gatewayPid, gatewayPort, mcpHttpPort, mcpWsPort });
|
|
263
|
-
}
|
|
264
|
-
// ─── 主安装入口 ───────────────────────────────────────────────────────────────
|
|
265
|
-
/**
|
|
266
|
-
* 执行安装流程(异步,日志实时写入 res)
|
|
267
|
-
*/
|
|
268
|
-
function runInstall(res, version) {
|
|
269
|
-
exports.installState = { status: 'downloading', message: '正在安装...', startTime: new Date().toISOString() };
|
|
270
|
-
(0, reportLog_1.log)(`开始安装 ${config_1.NPM_PACKAGE_NAME}@${version}`);
|
|
271
|
-
(async () => {
|
|
272
|
-
try {
|
|
273
|
-
// 1. 确保目录存在
|
|
274
|
-
ensureClawDir();
|
|
275
|
-
// 写入 pip.conf 配置文件
|
|
276
|
-
ensurePipConf();
|
|
277
|
-
// 安装开始时启动 workspace 监控,自动清除新文件的 Gatekeeper 隔离属性
|
|
278
|
-
await (0, quarantine_1.watchWorkspaceQuarantine)().catch((err) => {
|
|
279
|
-
(0, reportLog_1.log)(`[quarantine] watchWorkspaceQuarantine 启动异常: ${err.message}`);
|
|
280
|
-
});
|
|
281
|
-
// 耗时步骤总数:installMcporter, npmDownloadQBotClaw, fullScanClearQuarantine, npmRebuildQBotClaw, configAndRunQBotClaw
|
|
282
|
-
const totalSteps = 5;
|
|
283
|
-
let currentStep = 0;
|
|
284
|
-
// 2. 全局安装 mcporter(已安装则跳过)
|
|
285
|
-
sendProgress(res, totalSteps, currentStep, `开始安装 mcporter...`);
|
|
286
|
-
await installMcporter(res);
|
|
287
|
-
currentStep++;
|
|
288
|
-
// 3. 下载解压 QBotClaw(--ignore-scripts,不执行 postinstall 脚本)
|
|
289
|
-
sendProgress(res, totalSteps, currentStep, `开始下载解压 QBotClaw...`);
|
|
290
|
-
await npmDownloadQBotClaw(version);
|
|
291
|
-
currentStep++;
|
|
292
|
-
sendProgress(res, totalSteps, currentStep, `npmDownloadQBotClaw 完成`);
|
|
293
|
-
// 4. 全量扫描清除隔离属性(确保所有 dylib/node/可执行文件在 rebuild 前已安全)
|
|
294
|
-
await (0, quarantine_1.fullScanClearQuarantine)('npm install --ignore-scripts 完成后,rebuild 前');
|
|
295
|
-
currentStep++;
|
|
296
|
-
// 5. 执行 npm rebuild,触发所有 postinstall 脚本
|
|
297
|
-
sendProgress(res, totalSteps, currentStep, `开始 npm rebuild...`);
|
|
298
|
-
await npmRebuildQBotClaw(version);
|
|
299
|
-
currentStep++;
|
|
300
|
-
sendProgress(res, totalSteps, currentStep, `npmRebuildQBotClaw 完成`);
|
|
301
|
-
// 安装结束,停止 workspace 监控
|
|
302
|
-
await (0, quarantine_1.stopWatchWorkspaceQuarantine)().catch((err) => {
|
|
303
|
-
(0, reportLog_1.log)(`[quarantine] stopWatchWorkspaceQuarantine 异常: ${err.message}`);
|
|
304
|
-
});
|
|
305
|
-
exports.installState = { status: 'success', message: '安装成功', endTime: new Date().toISOString() };
|
|
306
|
-
(0, reportLog_1.log)(`安装成功`);
|
|
307
|
-
(0, reportLog_1.refreshClawVersion)(version);
|
|
308
|
-
// 6. 配置并启动 QBotClaw gateway
|
|
309
|
-
sendProgress(res, totalSteps, currentStep, `开始配置并启动 QBotClaw gateway...`);
|
|
310
|
-
await configAndRunQBotClaw();
|
|
311
|
-
currentStep++;
|
|
312
|
-
if (res.writable) {
|
|
313
|
-
try {
|
|
314
|
-
res.end(JSON.stringify({ status: 'done', progress: 100, message: '安装成功' }) + '\n');
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
catch (err) {
|
|
321
|
-
// 安装失败时也要停止监控
|
|
322
|
-
await (0, quarantine_1.stopWatchWorkspaceQuarantine)().catch(() => { });
|
|
323
|
-
exports.installState = { status: 'failed', message: err.message, endTime: new Date().toISOString() };
|
|
324
|
-
(0, reportLog_1.log)(`安装失败: ${err.message}`);
|
|
325
|
-
if (res && res.writable) {
|
|
326
|
-
try {
|
|
327
|
-
res.end(JSON.stringify({ status: 'failed', progress: 0, message: err.message }) + '\n');
|
|
328
|
-
}
|
|
329
|
-
catch {
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
})();
|
|
334
|
-
}
|
|
335
|
-
//# sourceMappingURL=installer.js.map
|
|
1
|
+
"use strict";var d=exports&&exports.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.PYTHON_DIR=exports.installState=void 0,exports.runQBotClaw=C,exports.configAndRunQBotClaw=m,exports.runInstall=y;const l=d(require("fs")),u=d(require("path")),p=require("child_process"),s=require("./config"),w=require("./utils"),_=require("./state"),f=require("./quarantine"),a=require("./reportLog");function c(t,e,r,o){if(t&&t.writable){const n=e>0?Math.round(r/e*1e4)/100:0;try{t.write(JSON.stringify({status:"downloading",progress:n,totalSteps:e,currentStep:r,message:o})+`
|
|
2
|
+
`)}catch{}}}exports.installState={status:"idle",message:"\u672A\u5F00\u59CB"},exports.PYTHON_DIR=u.default.join(s.QBOT_CLAW_DIR,"python");function C(t,e){return new Promise(r=>{const o=(0,p.spawn)(s.QBOTCLAW_BIN,t,{stdio:["ignore","pipe","pipe"],env:{PATH:s.NODE_BIN_DIR,HOME:process.env.HOME,OPENCLAW_TELEMETRY_GALIELO_GUID:process.env.QB_GUID||"",OPENCLAW_TELEMETRY_GALIELO_QIEMI36:process.env.QB_QIMEI36||""}});(0,a.log)(`[QBotClaw] \u6267\u884C: QBotClaw ${t.join(" ")}`),o.stdout.on("data",n=>{const i=n.toString();if(e&&e.writable)try{e.write(i)}catch{}}),o.stderr.on("data",n=>{const i=n.toString();if(e&&e.writable)try{e.write(i)}catch{}}),o.on("close",n=>{(0,a.log)(`[QBotClaw] \u6267\u884C\u5B8C\u6210\uFF0C\u9000\u51FA\u7801: ${n}`),r()}),o.on("error",n=>{(0,a.log)(`[QBotClaw] \u542F\u52A8\u5931\u8D25: ${n.message}`),r()})})}function A(){l.default.existsSync(s.QBOT_CLAW_DIR)||l.default.mkdirSync(s.QBOT_CLAW_DIR,{recursive:!0})}function N(){const t=u.default.join(exports.PYTHON_DIR,"pip.conf");if(l.default.existsSync(t)){(0,a.log)("pip.conf \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7");return}const e=["[global]","index-url = https://mirrors.tencent.com/pypi/simple/",`cache-dir = ${u.default.join(exports.PYTHON_DIR,".pip-cache")}`,"","[install]",`prefix = ${exports.PYTHON_DIR}`,""].join(`
|
|
3
|
+
`);l.default.writeFileSync(t,e,"utf-8"),(0,a.log)(`\u5DF2\u5199\u5165 pip.conf: ${t}`)}async function I(t){if(l.default.existsSync(s.MCPORTER_BIN)){(0,a.log)("mcporter \u5DF2\u5B89\u88C5\uFF0C\u8DF3\u8FC7");return}(0,a.log)("\u5F00\u59CB\u5B89\u88C5 mcporter..."),await new Promise((e,r)=>{const o=(0,p.spawn)(s.NPM_BIN,["--userconfig",s.NPM_RC,"install","-g","mcporter"],{stdio:["ignore","pipe","pipe"],env:{...process.env,PATH:`${u.default.dirname(s.NPM_BIN)}:${process.env.PATH}`}});o.stdout.on("data",n=>{const i=`[mcporter] ${n.toString().trim()}`;(0,a.log)(i),c(t,0,0,i)}),o.stderr.on("data",n=>{const i=`[mcporter] ${n.toString().trim()}`;(0,a.log)(i),c(t,0,0,i)}),o.on("close",n=>{n===0?((0,a.log)("mcporter \u5B89\u88C5\u6210\u529F"),e()):r(new Error(`mcporter \u5B89\u88C5\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${n}`))}),o.on("error",r)})}function P(t,e){return new Promise((r,o)=>{const n=(0,p.spawn)(s.NPM_BIN,["--userconfig",s.NPM_RC,...t],{stdio:["ignore","pipe","pipe"],env:{...process.env,PATH:`${u.default.dirname(s.NPM_BIN)}:${process.env.PATH}`}});n.stdout.on("data",i=>{const g=`[npm] ${i.toString().trim()}`;(0,a.log)(g)}),n.stderr.on("data",i=>{const g=`[npm] ${i.toString().trim()}`;(0,a.log)(g)}),n.on("close",i=>{i===0?((0,a.log)(`${e} \u6210\u529F`),r()):o(new Error(`${e} \u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${i}`))}),n.on("error",o)})}async function O(t){const e=`${s.NPM_PACKAGE_NAME}@${t}`;(0,a.log)(`\u5F00\u59CB\u5B89\u88C5 ${e}\uFF08--ignore-scripts\uFF0C\u4EC5\u4E0B\u8F7D\u89E3\u538B\uFF09...`),await P(["install","-g","--ignore-scripts",e],`${e} \u4E0B\u8F7D\u89E3\u538B`)}async function $(t){const e=`${s.NPM_PACKAGE_NAME}@${t}`;(0,a.log)("\u5F00\u59CB npm rebuild\uFF08\u6267\u884C postinstall \u811A\u672C\uFF09..."),await P(["rebuild","-g",s.NPM_PACKAGE_NAME],`${e} rebuild`),(0,a.log)(`${e} \u5B89\u88C5\u6210\u529F`)}async function E(t,e,r){const o=u.default.join(s.NPM_GLOBAL_MODULES,s.NPM_PACKAGE_NAME,"preset-config.json");let n;if(l.default.existsSync(s.QBOT_CLAW_CONFIG_PATH))n=s.QBOT_CLAW_CONFIG_PATH,(0,a.log)(`\u4F7F\u7528\u73B0\u6709\u914D\u7F6E\u6587\u4EF6: ${n}`);else if(l.default.existsSync(o))n=o,(0,a.log)(`\u73B0\u6709\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528 npm \u5168\u5C40\u5B89\u88C5\u76EE\u5F55\u4E2D\u7684\u914D\u7F6E: ${n}`);else{(0,a.log)(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u914D\u7F6E\u5199\u5165: ${s.QBOT_CLAW_CONFIG_PATH}`);return}let i;try{i=JSON.parse(l.default.readFileSync(n,"utf-8")),(0,a.log)(`\u5DF2\u8BFB\u53D6\u914D\u7F6E: ${n}`)}catch(g){(0,a.log)(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${n}\uFF0C${g.message}\uFF0C\u8DF3\u8FC7\u914D\u7F6E\u5199\u5165`);return}t!==void 0&&(i.gateway={...i.gateway??{},port:t},(0,a.log)(`\u5DF2\u5C06 gateway.port=${t} \u5199\u5165\u914D\u7F6E`)),i.plugins=i.plugins??{},i.plugins.entries=i.plugins.entries??{},i.plugins.entries["x5use-browser-control"]=i.plugins.entries["x5use-browser-control"]??{},i.plugins.entries["x5use-browser-control"].config={...i.plugins.entries["x5use-browser-control"].config??{},wsPort:r,httpPort:e},(0,a.log)(`\u5DF2\u5C06 x5use-browser-control wsPort=${r}, httpPort=${e} \u5199\u5165\u914D\u7F6E`),l.default.writeFileSync(s.QBOT_CLAW_CONFIG_PATH,JSON.stringify(i,null,2),"utf-8"),(0,a.log)(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5199\u5165: ${s.QBOT_CLAW_CONFIG_PATH}`)}async function m(){const t=(0,_.readState)();let e;t.gatewayPort&&!await(0,_.isPortAvailable)(t.gatewayPort)?(e=t.gatewayPort,(0,a.log)(`\u68C0\u6D4B\u5230 QBotClaw gateway \u5DF2\u5728\u7AEF\u53E3 ${e} \u8FD0\u884C\uFF0C\u590D\u7528\u539F\u7AEF\u53E3`)):e=await(0,w.findAvailablePort)(),(0,a.log)(`\u540E\u53F0\u542F\u52A8 QBotClaw gateway run --port ${e} --force`);const r=await(0,w.findAvailablePort)(),o=await(0,w.findAvailablePort)();await E(e,r,o);const n=(0,p.spawn)(s.QBOTCLAW_BIN,["gateway","run","--port",String(e),"--force"],{stdio:"ignore",env:{PATH:s.NODE_BIN_DIR,HOME:process.env.HOME,OPENCLAW_TELEMETRY_GALIELO_GUID:process.env.QB_GUID||"",OPENCLAW_TELEMETRY_GALIELO_QIEMI36:process.env.QB_QIMEI36||""}});n.unref();const i=n.pid;(0,a.log)(`QBotClaw gateway \u5DF2\u5728\u540E\u53F0\u542F\u52A8\uFF0CPID: ${i}\uFF0C\u7AEF\u53E3: ${e}`),(0,_.writeState)({...(0,_.readState)(),gatewayPid:i,gatewayPort:e,mcpHttpPort:r,mcpWsPort:o})}function y(t,e){exports.installState={status:"downloading",message:"\u6B63\u5728\u5B89\u88C5...",startTime:new Date().toISOString()},(0,a.log)(`\u5F00\u59CB\u5B89\u88C5 ${s.NPM_PACKAGE_NAME}@${e}`),(async()=>{try{A(),N(),await(0,f.watchWorkspaceQuarantine)().catch(n=>{(0,a.log)(`[quarantine] watchWorkspaceQuarantine \u542F\u52A8\u5F02\u5E38: ${n.message}`)});const r=5;let o=0;if(c(t,r,o,"\u5F00\u59CB\u5B89\u88C5 mcporter..."),await I(t),o++,c(t,r,o,"\u5F00\u59CB\u4E0B\u8F7D\u89E3\u538B QBotClaw..."),await O(e),o++,c(t,r,o,"npmDownloadQBotClaw \u5B8C\u6210"),await(0,f.fullScanClearQuarantine)("npm install --ignore-scripts \u5B8C\u6210\u540E\uFF0Crebuild \u524D"),o++,c(t,r,o,"\u5F00\u59CB npm rebuild..."),await $(e),o++,c(t,r,o,"npmRebuildQBotClaw \u5B8C\u6210"),await(0,f.stopWatchWorkspaceQuarantine)().catch(n=>{(0,a.log)(`[quarantine] stopWatchWorkspaceQuarantine \u5F02\u5E38: ${n.message}`)}),exports.installState={status:"success",message:"\u5B89\u88C5\u6210\u529F",endTime:new Date().toISOString()},(0,a.log)("\u5B89\u88C5\u6210\u529F"),(0,a.refreshClawVersion)(e),c(t,r,o,"\u5F00\u59CB\u914D\u7F6E\u5E76\u542F\u52A8 QBotClaw gateway..."),await m(),o++,t.writable)try{t.end(JSON.stringify({status:"done",progress:100,message:"\u5B89\u88C5\u6210\u529F"})+`
|
|
4
|
+
`)}catch{}}catch(r){if(await(0,f.stopWatchWorkspaceQuarantine)().catch(()=>{}),exports.installState={status:"failed",message:r.message,endTime:new Date().toISOString()},(0,a.log)(`\u5B89\u88C5\u5931\u8D25: ${r.message}`),t&&t.writable)try{t.end(JSON.stringify({status:"failed",progress:0,message:r.message})+`
|
|
5
|
+
`)}catch{}}})()}
|
package/dist/quarantine.d.ts
CHANGED
|
@@ -1,37 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 全量递归扫描目录,对所有匹配白名单的文件清除 quarantine 属性。
|
|
3
|
-
*
|
|
4
|
-
* 设计目标:作为 watcher 的补偿机制。当 FSEvents 丢弃事件时,
|
|
5
|
-
* 通过全量扫描确保不会遗漏任何需要处理的文件。
|
|
6
|
-
*
|
|
7
|
-
* 性能(基于 benchmark):
|
|
8
|
-
* - 遍历 ~60000 文件 + 过滤 ~3400 匹配文件 + 逐个 removeAttribute ≈ 300ms
|
|
9
|
-
* - 对用户无感知,可安全地在 npm install 过程中触发
|
|
10
|
-
*
|
|
11
|
-
* @param reason 触发原因,用于日志记录
|
|
12
|
-
*/
|
|
13
1
|
export declare function fullScanClearQuarantine(reason: string): Promise<void>;
|
|
14
|
-
/**
|
|
15
|
-
* 停止监听 QBOT_CLAW_DIR 目录,取消 quarantine 属性清除的 watcher。
|
|
16
|
-
*/
|
|
17
2
|
export declare function stopWatchWorkspaceQuarantine(): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* 监听 QBOT_CLAW_DIR 目录,自动清除新文件的 Gatekeeper 隔离属性。
|
|
20
|
-
*
|
|
21
|
-
* 核心设计:使用 @parcel/watcher(底层直接调用 FSEvents C API)。
|
|
22
|
-
* 相比 Node.js 原生 fs.watch,@parcel/watcher 的事件投递延迟低至 ~10ms,
|
|
23
|
-
* 可以在 npm postinstall 脚本启动二进制文件之前完成 quarantine 属性清除。
|
|
24
|
-
*
|
|
25
|
-
* 优势:
|
|
26
|
-
* - 超低延迟(~10ms vs fs.watch 的数秒延迟)
|
|
27
|
-
* - 精确事件类型(create/update/delete),无需猜测 rename 语义
|
|
28
|
-
* - 原生支持 glob ignore 模式,在 C++ 层过滤无关事件
|
|
29
|
-
* - 整棵目录树只需要 1 个文件描述符
|
|
30
|
-
*
|
|
31
|
-
* 策略:
|
|
32
|
-
* - 只处理 create 事件(文件新增),忽略 update/delete
|
|
33
|
-
* - 通过路径黑名单排除无关目录(纯字符串匹配,零 IO)
|
|
34
|
-
* - 通过扩展名白名单过滤需要处理的文件(.dylib, .so, .node, 无扩展名)
|
|
35
|
-
*/
|
|
36
3
|
export declare function watchWorkspaceQuarantine(): Promise<void>;
|
|
37
|
-
//# sourceMappingURL=quarantine.d.ts.map
|