reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = { python: 'python', installDir: '', install: false, installPackage: false, json: false, markdown: false };
|
||||
for (let i = 2; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a === '--python') args.python = argv[++i] || 'python';
|
||||
else if (a === '--install-dir') args.installDir = argv[++i] || '';
|
||||
else if (a === '--install') args.install = true;
|
||||
else if (a === '--install-package') args.installPackage = true;
|
||||
else if (a === '--json') args.json = true;
|
||||
else if (a === '--markdown') args.markdown = true;
|
||||
else if (a === '--help' || a === '-h') args.help = true;
|
||||
else throw new Error(`未知参数:${a}`);
|
||||
}
|
||||
if (!args.json && !args.markdown) args.markdown = true;
|
||||
return args;
|
||||
}
|
||||
|
||||
function usage() {
|
||||
return `用法:
|
||||
node scripts/install_ruyipage_runtime.js --python python --install-dir <ruyipage-browsers-dir> --markdown
|
||||
node scripts/install_ruyipage_runtime.js --python python --install-dir <ruyipage-browsers-dir> --install --markdown
|
||||
node scripts/install_ruyipage_runtime.js --python python --install-dir <ruyipage-browsers-dir> --install-package --install --markdown
|
||||
|
||||
说明:默认只输出安装计划,不执行下载 / 安装。只有用户明确确认并提供安装目录后,才添加 --install。
|
||||
注意:--install-package 会修改当前 Python 环境,必须先获得用户确认。`;
|
||||
}
|
||||
|
||||
function run(cmd, args, timeout = 120000) {
|
||||
const ret = spawnSync(cmd, args, { encoding: 'utf8', timeout, windowsHide: true });
|
||||
return {
|
||||
ok: ret.status === 0,
|
||||
status: ret.status,
|
||||
stdout: (ret.stdout || '').trim(),
|
||||
stderr: (ret.stderr || '').trim(),
|
||||
error: ret.error ? ret.error.message : '',
|
||||
command: [cmd].concat(args.map(quoteIfNeeded)).join(' '),
|
||||
};
|
||||
}
|
||||
|
||||
function quoteIfNeeded(s) {
|
||||
s = String(s);
|
||||
return /\s/.test(s) ? `"${s.replace(/"/g, '\\"')}"` : s;
|
||||
}
|
||||
|
||||
function exists(p) {
|
||||
try { return !!p && fs.existsSync(p); } catch { return false; }
|
||||
}
|
||||
|
||||
function checkPackage(python) {
|
||||
const code = 'import ruyipage, json; print(json.dumps({"ok": True, "version": getattr(ruyipage, "__version__", "")}, ensure_ascii=False))';
|
||||
const ret = run(python, ['-c', code], 20000);
|
||||
if (!ret.ok) return { ok: false, version: '', error: ret.stderr || ret.error || ret.stdout };
|
||||
try {
|
||||
const parsed = JSON.parse(ret.stdout.replace(/^\uFEFF/, ''));
|
||||
return { ok: true, version: parsed.version || '', error: '' };
|
||||
} catch (err) {
|
||||
return { ok: false, version: '', error: err.message || String(err) };
|
||||
}
|
||||
}
|
||||
|
||||
function assertInstallDir(dir) {
|
||||
if (!dir) throw new Error('必须提供 --install-dir;不要在未确认目录时安装 ruyiPage 定制 Firefox runtime。');
|
||||
const resolved = path.resolve(dir);
|
||||
const parsed = path.parse(resolved);
|
||||
if (resolved === parsed.root) throw new Error('安装目录不能是磁盘根目录。请提供专用目录,例如 <工作目录>/tools/ruyipage-browsers。');
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function verifyAfterInstall(python, installDir) {
|
||||
const script = path.join(__dirname, 'check_external_tools.js');
|
||||
const ret = run(process.execPath, [script, '--python', python, '--ruyipage-install-dir', installDir, '--json'], 60000);
|
||||
let parsed = null;
|
||||
try { parsed = JSON.parse(ret.stdout.replace(/^\uFEFF/, '')); } catch { parsed = null; }
|
||||
return { command: ret.command, ok: ret.ok, stdout: ret.stdout, stderr: ret.stderr || ret.error, parsed };
|
||||
}
|
||||
|
||||
function commandPlan(args, installDir, pkg) {
|
||||
const commands = [];
|
||||
if (!pkg.ok) commands.push(`${args.python} -m pip install ruyiPage requests --upgrade`);
|
||||
else commands.push(`${args.python} -m pip install requests --upgrade`);
|
||||
commands.push(`${args.python} -m ruyipage install --install-dir "${installDir}"`);
|
||||
commands.push(`node scripts/check_external_tools.js --python ${args.python} --ruyipage-install-dir "${installDir}" --markdown`);
|
||||
return commands;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = parseArgs(process.argv);
|
||||
if (args.help) return { help: usage() };
|
||||
const installDir = assertInstallDir(args.installDir);
|
||||
const pkgBefore = checkPackage(args.python);
|
||||
const result = {
|
||||
dryRun: !args.install,
|
||||
python: args.python,
|
||||
installDir,
|
||||
packageBefore: pkgBefore,
|
||||
packageInstallAttempted: false,
|
||||
runtimeInstallAttempted: false,
|
||||
commands: commandPlan(args, installDir, pkgBefore),
|
||||
steps: [],
|
||||
verify: null,
|
||||
success: false,
|
||||
};
|
||||
|
||||
if (!args.install) {
|
||||
result.steps.push('当前为安装计划,不执行下载或安装。请用户确认后再添加 --install。');
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
fs.mkdirSync(installDir, { recursive: true });
|
||||
|
||||
let pkg = pkgBefore;
|
||||
if (!pkg.ok && args.installPackage) {
|
||||
result.packageInstallAttempted = true;
|
||||
const pip = run(args.python, ['-m', 'pip', 'install', 'ruyiPage', 'requests', '--upgrade'], 180000);
|
||||
result.steps.push(`安装 ruyiPage Python 包与 requests 依赖:${pip.ok ? '成功' : '失败'}\n${pip.stdout || pip.stderr || pip.error}`.trim());
|
||||
pkg = checkPackage(args.python);
|
||||
} else if (pkg.ok && args.installPackage) {
|
||||
result.packageInstallAttempted = true;
|
||||
const pip = run(args.python, ['-m', 'pip', 'install', 'requests', '--upgrade'], 180000);
|
||||
result.steps.push(`安装 / 更新 smart_fingerprint requests 依赖:${pip.ok ? '成功' : '失败'}\n${pip.stdout || pip.stderr || pip.error}`.trim());
|
||||
}
|
||||
|
||||
if (!pkg.ok) {
|
||||
result.steps.push('未安装 ruyiPage Python 包,已停止 runtime 安装。若用户确认允许修改 Python 环境,请重新添加 --install-package。');
|
||||
result.verify = verifyAfterInstall(args.python, installDir);
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.runtimeInstallAttempted = true;
|
||||
const install = run(args.python, ['-m', 'ruyipage', 'install', '--install-dir', installDir], 900000);
|
||||
result.steps.push(`安装 ruyiPage 定制 Firefox runtime:${install.ok ? '成功' : '失败'}\n${install.stdout || install.stderr || install.error}`.trim());
|
||||
result.verify = verifyAfterInstall(args.python, installDir);
|
||||
result.success = !!(result.verify && result.verify.parsed && result.verify.parsed.ruyiPage && result.verify.parsed.ruyiPage.managedRuntimeVerified);
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderMarkdown(result) {
|
||||
if (result.help) return result.help + '\n';
|
||||
const lines = ['# ruyiPage 定制 Firefox runtime 安装流程', ''];
|
||||
lines.push(`- dry-run:${result.dryRun ? '是' : '否'}`);
|
||||
lines.push(`- Python:${result.python}`);
|
||||
lines.push(`- 安装目录:${result.installDir}`);
|
||||
lines.push(`- ruyiPage Python 包:${result.packageBefore.ok ? '已安装' : '未检测到'}`);
|
||||
if (result.packageBefore.version) lines.push(`- ruyiPage 版本:${result.packageBefore.version}`);
|
||||
lines.push('', '## 将执行 / 已执行的命令');
|
||||
for (const cmd of result.commands) lines.push('- `' + cmd + '`');
|
||||
if (result.steps.length) {
|
||||
lines.push('', '## 执行记录');
|
||||
for (const step of result.steps) lines.push(`- ${step.replace(/\n/g, '\n ')}`);
|
||||
}
|
||||
if (result.verify) {
|
||||
lines.push('', '## 安装后验证');
|
||||
lines.push('- 验证命令:`' + result.verify.command + '`');
|
||||
lines.push(`- 验证命令是否成功:${result.verify.ok ? '是' : '否'}`);
|
||||
const rp = result.verify.parsed && result.verify.parsed.ruyiPage;
|
||||
if (rp) {
|
||||
lines.push(`- 定制 Firefox runtime 是否通过验证:${rp.managedRuntimeVerified ? '是' : '否'}`);
|
||||
if (rp.runtimeExecutable) lines.push(`- 已验证 Firefox:${rp.runtimeExecutable}`);
|
||||
if (rp.conclusion) lines.push(`- 结论:${rp.conclusion}`);
|
||||
} else if (result.verify.stderr) {
|
||||
lines.push(`- 验证错误:${result.verify.stderr}`);
|
||||
}
|
||||
}
|
||||
lines.push('', `## 结果:${result.success ? '通过' : '未通过 / 待用户确认'}`);
|
||||
if (result.dryRun) lines.push('- 当前仅输出计划;若用户确认未安装并提供该目录,再运行带 --install 的命令。');
|
||||
return lines.join('\n') + '\n';
|
||||
}
|
||||
|
||||
try {
|
||||
const args = parseArgs(process.argv);
|
||||
const result = args.help ? { help: usage() } : main();
|
||||
if (args.json) console.log(JSON.stringify(result, null, 2));
|
||||
if (args.markdown) process.stdout.write(renderMarkdown(result));
|
||||
} catch (err) {
|
||||
console.error(err.message || String(err));
|
||||
console.error(usage());
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user