dev/zhi #1
176
install.mjs
176
install.mjs
@@ -12,8 +12,8 @@
|
||||
* node install.mjs --uninstall --prefix /usr/local
|
||||
*/
|
||||
|
||||
import { execSync, spawn } from 'child_process';
|
||||
import { existsSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, chmodSync } from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, mkdirSync, copyFileSync, writeFileSync, chmodSync } from 'fs';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { homedir, platform } from 'os';
|
||||
@@ -53,7 +53,7 @@ function log(message, color = 'reset') {
|
||||
}
|
||||
|
||||
function logStep(step, message) {
|
||||
log(`[${step}/7] ${message}`, 'cyan');
|
||||
log(`[${step}/6] ${message}`, 'cyan');
|
||||
}
|
||||
|
||||
function logSuccess(message) {
|
||||
@@ -90,36 +90,6 @@ async function prompt(question) {
|
||||
});
|
||||
}
|
||||
|
||||
async function promptPassword(question) {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
// Hide password input
|
||||
const stdin = process.stdin;
|
||||
stdin.on('data', (char) => {
|
||||
char = char.toString();
|
||||
switch (char) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\u0004':
|
||||
stdin.pause();
|
||||
break;
|
||||
default:
|
||||
process.stdout.write('*');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
rl.question(question, (answer) => {
|
||||
rl.close();
|
||||
console.log(); // New line after password
|
||||
resolve(answer.trim());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Step 1: Environment Detection
|
||||
// ============================================================================
|
||||
@@ -248,7 +218,6 @@ async function buildComponents(env) {
|
||||
silent: !options.verbose
|
||||
});
|
||||
|
||||
// Verify binary exists
|
||||
const binaryPath = join(passMgrDir, 'dist', 'pass_mgr');
|
||||
if (!existsSync(binaryPath)) {
|
||||
throw new Error('pass_mgr binary not found after build');
|
||||
@@ -308,16 +277,14 @@ async function installComponents(env) {
|
||||
|
||||
logStep(4, 'Installing components...');
|
||||
|
||||
// Determine install paths
|
||||
const installDir = options.prefix || env.openclawDir;
|
||||
const binDir = options.prefix ? join(installDir, 'bin') : join(installDir, 'bin');
|
||||
const binDir = join(installDir, 'bin');
|
||||
const skillsDir = join(installDir, 'skills', 'paddedcell');
|
||||
|
||||
log(` Install directory: ${installDir}`, 'blue');
|
||||
log(` Binary directory: ${binDir}`, 'blue');
|
||||
log(` Skills directory: ${skillsDir}`, 'blue');
|
||||
|
||||
// Create directories
|
||||
mkdirSync(binDir, { recursive: true });
|
||||
mkdirSync(skillsDir, { recursive: true });
|
||||
|
||||
@@ -370,12 +337,6 @@ async function installComponents(env) {
|
||||
JSON.stringify(manifest, null, 2)
|
||||
);
|
||||
logSuccess('Skill manifest created');
|
||||
|
||||
// Add to PATH if needed
|
||||
if (options.prefix) {
|
||||
log('\n To use pass_mgr, add the following to your shell profile:', 'yellow');
|
||||
log(` export PATH="${binDir}:$PATH"`, 'cyan');
|
||||
}
|
||||
}
|
||||
|
||||
function copyModule(source, dest) {
|
||||
@@ -399,12 +360,10 @@ function copyModule(source, dest) {
|
||||
} else {
|
||||
copyFileSync(srcPath, destPath);
|
||||
}
|
||||
} catch (err) {
|
||||
// Fallback: try to copy as file first, then directory
|
||||
} catch {
|
||||
try {
|
||||
copyFileSync(srcPath, destPath);
|
||||
} catch {
|
||||
// If file copy fails, try directory copy
|
||||
execSync(`cp -r "${srcPath}" "${destPath}"`, { cwd: __dirname });
|
||||
}
|
||||
}
|
||||
@@ -423,9 +382,8 @@ async function configure(env) {
|
||||
|
||||
logStep(5, 'Configuration...');
|
||||
|
||||
const passMgrPath = options.prefix
|
||||
? join(options.prefix, 'bin', 'pass_mgr')
|
||||
: join(env.openclawDir, 'bin', 'pass_mgr');
|
||||
const installDir = env.openclawDir || join(homedir(), '.openclaw');
|
||||
const passMgrPath = join(installDir, 'bin', 'pass_mgr');
|
||||
|
||||
// Check if already initialized
|
||||
const adminKeyDir = join(homedir(), '.pass_mgr');
|
||||
@@ -435,27 +393,9 @@ async function configure(env) {
|
||||
logSuccess('pass_mgr already initialized');
|
||||
} else {
|
||||
log(' pass_mgr not initialized yet.', 'yellow');
|
||||
log(' Run "pass_mgr admin init" manually after installation.', 'cyan');
|
||||
log(` Run "${passMgrPath} admin init" manually after installation.`, 'cyan');
|
||||
}
|
||||
|
||||
// Create environment config
|
||||
log(' Creating environment configuration...', 'blue');
|
||||
const installDir = env.openclawDir || join(homedir(), '.openclaw');
|
||||
const envConfig = [
|
||||
'# PaddedCell Environment Configuration',
|
||||
`export PATH="${dirname(passMgrPath)}:$PATH"`,
|
||||
'export PASS_MGR_PATH="pass_mgr"',
|
||||
'',
|
||||
'# PaddedCell skills',
|
||||
].join('\n');
|
||||
|
||||
const envFile = join(installDir, 'paddedcell.env');
|
||||
writeFileSync(envFile, envConfig);
|
||||
logSuccess(`Environment config written to ${envFile}`);
|
||||
|
||||
log('\n Add the following to your shell profile:', 'yellow');
|
||||
log(` source ${envFile}`, 'cyan');
|
||||
|
||||
// Update OpenClaw plugins.load.paths configuration
|
||||
log('\n Updating OpenClaw plugin configuration...', 'blue');
|
||||
|
||||
@@ -492,72 +432,14 @@ async function configure(env) {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Step 6: Verify Installation
|
||||
// ============================================================================
|
||||
|
||||
async function verifyInstallation(env) {
|
||||
if (options.buildOnly) {
|
||||
logStep(6, 'Skipping verification (--build-only)');
|
||||
return true;
|
||||
}
|
||||
|
||||
logStep(6, 'Verifying installation...');
|
||||
|
||||
let allOk = true;
|
||||
|
||||
// Check pass_mgr
|
||||
try {
|
||||
const passMgrPath = options.prefix
|
||||
? join(options.prefix, 'bin', 'pass_mgr')
|
||||
: join(env.openclawDir, 'bin', 'pass_mgr');
|
||||
|
||||
if (existsSync(passMgrPath)) {
|
||||
execSync(`"${passMgrPath}" --help`, { silent: true });
|
||||
logSuccess('pass_mgr is working');
|
||||
} else {
|
||||
logError('pass_mgr binary not found');
|
||||
allOk = false;
|
||||
}
|
||||
} catch {
|
||||
logError('pass_mgr test failed');
|
||||
allOk = false;
|
||||
}
|
||||
|
||||
// Check pcexec
|
||||
const pcexecPath = join(
|
||||
options.prefix || env.openclawDir,
|
||||
'skills', 'paddedcell', 'pcexec', 'dist', 'index.js'
|
||||
);
|
||||
if (existsSync(pcexecPath)) {
|
||||
logSuccess('pcexec module installed');
|
||||
} else {
|
||||
logError('pcexec module not found');
|
||||
allOk = false;
|
||||
}
|
||||
|
||||
// Check safe-restart
|
||||
const safeRestartPath = join(
|
||||
options.prefix || env.openclawDir,
|
||||
'skills', 'paddedcell', 'safe-restart', 'dist', 'index.js'
|
||||
);
|
||||
if (existsSync(safeRestartPath)) {
|
||||
logSuccess('safe-restart module installed');
|
||||
} else {
|
||||
logError('safe-restart module not found');
|
||||
allOk = false;
|
||||
}
|
||||
|
||||
return allOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Step 7: Print Summary
|
||||
// Step 6: Print Summary
|
||||
// ============================================================================
|
||||
|
||||
function printSummary(env) {
|
||||
logStep(7, 'Installation Summary');
|
||||
logStep(6, 'Installation Summary');
|
||||
|
||||
const installDir = options.prefix || env.openclawDir;
|
||||
const passMgrPath = join(installDir, 'bin', 'pass_mgr');
|
||||
|
||||
console.log('');
|
||||
log('╔════════════════════════════════════════════════════════╗', 'cyan');
|
||||
@@ -574,22 +456,19 @@ function printSummary(env) {
|
||||
log(` • safe-restart: ${join(__dirname, 'safe-restart', 'dist')}`, 'reset');
|
||||
} else {
|
||||
log('Installed components:', 'blue');
|
||||
log(` • pass_mgr binary: ${join(installDir, 'bin', 'pass_mgr')}`, 'reset');
|
||||
log(` • pass_mgr binary: ${passMgrPath}`, 'reset');
|
||||
log(` • pcexec module: ${join(installDir, 'skills', 'paddedcell', 'pcexec')}`, 'reset');
|
||||
log(` • safe-restart module: ${join(installDir, 'skills', 'paddedcell', 'safe-restart')}`, 'reset');
|
||||
console.log('');
|
||||
|
||||
log('Next steps:', 'blue');
|
||||
console.log('');
|
||||
log('1. Reload your shell or run:', 'yellow');
|
||||
log(` source ${join(installDir, 'paddedcell.env')}`, 'cyan');
|
||||
log('1. Initialize pass_mgr (required before first use):', 'yellow');
|
||||
log(` ${passMgrPath} admin init`, 'cyan');
|
||||
console.log('');
|
||||
log('2. Initialize pass_mgr (required before first use):', 'yellow');
|
||||
log(' pass_mgr admin init', 'cyan');
|
||||
console.log('');
|
||||
log('3. Test pass_mgr:', 'yellow');
|
||||
log(' pass_mgr set test_key mypass # Set a test password', 'cyan');
|
||||
log(' pass_mgr get test_key # Retrieve password', 'cyan');
|
||||
log('2. Test pass_mgr:', 'yellow');
|
||||
log(` ${passMgrPath} set test_key mypass # Set a test password`, 'cyan');
|
||||
log(` ${passMgrPath} get test_key # Retrieve password`, 'cyan');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
@@ -609,7 +488,6 @@ async function uninstall(env) {
|
||||
const installDir = options.prefix || env.openclawDir || join(homedir(), '.openclaw');
|
||||
const binDir = join(installDir, 'bin');
|
||||
const skillsDir = join(installDir, 'skills', 'paddedcell');
|
||||
const envFile = join(installDir, 'paddedcell.env');
|
||||
|
||||
const itemsToRemove = [];
|
||||
|
||||
@@ -623,10 +501,6 @@ async function uninstall(env) {
|
||||
itemsToRemove.push(skillsDir);
|
||||
}
|
||||
|
||||
if (existsSync(envFile)) {
|
||||
itemsToRemove.push(envFile);
|
||||
}
|
||||
|
||||
if (itemsToRemove.length === 0) {
|
||||
log('No installed components found.', 'yellow');
|
||||
return;
|
||||
@@ -647,7 +521,7 @@ async function uninstall(env) {
|
||||
// Perform uninstall
|
||||
for (const item of itemsToRemove) {
|
||||
try {
|
||||
if (item === passMgrBinary || item === envFile) {
|
||||
if (item === passMgrBinary) {
|
||||
execSync(`rm -f "${item}"`, { silent: true });
|
||||
logSuccess(`Removed: ${item}`);
|
||||
} else {
|
||||
@@ -704,12 +578,12 @@ async function uninstall(env) {
|
||||
log('║ PaddedCell Uninstall Complete ║', 'cyan');
|
||||
log('╚════════════════════════════════════════════════════════╝', 'cyan');
|
||||
console.log('');
|
||||
|
||||
log('Remember to remove the following from your shell profile:', 'yellow');
|
||||
log(` source ${envFile}`, 'cyan');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Main
|
||||
// ============================================================================
|
||||
|
||||
async function main() {
|
||||
console.log('');
|
||||
log('╔════════════════════════════════════════════════════════╗', 'cyan');
|
||||
@@ -730,12 +604,6 @@ async function main() {
|
||||
await buildComponents(env);
|
||||
await installComponents(env);
|
||||
await configure(env);
|
||||
const verified = await verifyInstallation(env);
|
||||
|
||||
if (!verified && !options.buildOnly) {
|
||||
log('\nInstallation completed with warnings.', 'yellow');
|
||||
}
|
||||
|
||||
printSummary(env);
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user