#!/usr/bin/env node /** * QwenClaw Installer for Qwen Code * * This script installs and configures QwenClaw as the default agent for Qwen Code. * * Usage: bun run install-qwenclaw.js */ import { spawn, execSync } from "child_process"; import { join } from "path"; import { existsSync, readFileSync, writeFile, mkdirSync } from "fs"; const QWENCLAW_DIR = join(process.env.HOME || process.env.USERPROFILE || "", "qwenclaw"); const QWEN_DIR = join(process.env.HOME || process.env.USERPROFILE || "", ".qwen"); const QWEN_SETTINGS_FILE = join(QWEN_DIR, "settings.json"); const QWEN_MCP_FILE = join(QWEN_DIR, "mcp.json"); const COLORS = { cyan: "\x1b[36m", green: "\x1b[32m", yellow: "\x1b[33m", red: "\x1b[31m", magenta: "\x1b[35m", reset: "\x1b[0m", }; function log(message, color = "reset") { console.log(`${COLORS[color]}${message}${COLORS.reset}`); } function showBanner() { log("\n" + "═".repeat(60), "cyan"); log(" 🐾 QWENCLAW INSTALLER FOR QWEN CODE", "cyan"); log("═".repeat(60), "cyan"); } /** * Check if QwenClaw is installed */ function checkInstallation() { log("\nšŸ“¦ Checking QwenClaw installation...", "cyan"); if (!existsSync(QWENCLAW_DIR)) { log("āŒ QwenClaw not found at: " + QWENCLAW_DIR, "red"); log("\nšŸ“„ Installing QwenClaw...", "yellow"); try { const repoUrl = "https://github.rommark.dev/admin/QwenClaw-with-Auth.git"; execSync(`git clone ${repoUrl} "${QWENCLAW_DIR}"`, { stdio: "inherit" }); log("āœ… QwenClaw cloned successfully", "green"); } catch (err) { log(`āŒ Failed to clone: ${err.message}`, "red"); process.exit(1); } } else { log("āœ… QwenClaw found at: " + QWENCLAW_DIR, "green"); } // Install dependencies log("\nšŸ“¦ Installing dependencies...", "cyan"); try { execSync(`cd "${QWENCLAW_DIR}" && bun install`, { stdio: "inherit" }); log("āœ… Dependencies installed", "green"); } catch (err) { log(`āŒ Failed to install dependencies: ${err.message}`, "red"); process.exit(1); } // Install Playwright browsers log("\n🌐 Installing Playwright browsers...", "cyan"); try { execSync(`cd "${QWENCLAW_DIR}" && npx playwright install chromium`, { stdio: "inherit" }); log("āœ… Playwright browsers installed", "green"); } catch (err) { log(`āš ļø Playwright installation skipped: ${err.message}`, "yellow"); } } /** * Create Qwen directory if needed */ function ensureQwenDir() { log("\nšŸ“ Ensuring Qwen directory exists...", "cyan"); if (!existsSync(QWEN_DIR)) { mkdirSync(QWEN_DIR, { recursive: true }); log("āœ… Created Qwen directory: " + QWEN_DIR, "green"); } else { log("āœ… Qwen directory exists", "green"); } // Create qwenclaw subdirectory const qwenclawDataDir = join(QWEN_DIR, "qwenclaw"); if (!existsSync(qwenclawDataDir)) { mkdirSync(qwenclawDataDir, { recursive: true }); log("āœ… Created QwenClaw data directory", "green"); } } /** * Update Qwen Code settings */ function updateSettings() { log("\nāš™ļø Configuring Qwen Code settings...", "cyan"); let settings = {}; if (existsSync(QWEN_SETTINGS_FILE)) { try { const content = readFileSync(QWEN_SETTINGS_FILE, "utf-8"); settings = JSON.parse(content); log("āœ… Loaded existing settings", "green"); } catch (err) { log(`āš ļø Warning: Could not parse settings.json`, "yellow"); } } // Configure skills if (!settings.skills) { settings.skills = {}; } if (!settings.skills.enabled) { settings.skills.enabled = []; } const requiredSkills = [ "qwenclaw-integration", "gui-automation", "qwenbot-integration", ]; for (const skill of requiredSkills) { if (!settings.skills.enabled.includes(skill)) { settings.skills.enabled.push(skill); log(` + Enabled skill: ${skill}`, "green"); } } settings.skills.default = "qwenclaw-integration"; log(" āœ“ Set default skill: qwenclaw-integration", "green"); // Configure agents if (!settings.agents) { settings.agents = {}; } settings.agents.default = "qwenclaw"; settings.agents.qwenclaw = { name: "QwenClaw", description: "Persistent AI assistant daemon with automation capabilities", autoStart: true, skills: ["qwenclaw-integration", "gui-automation", "qwenbot-integration"], }; log(" āœ“ Set default agent: qwenclaw", "green"); // Configure MCP servers if (!settings.mcpServers) { settings.mcpServers = {}; } settings.mcpServers.qwenclaw = { command: "bun", args: ["run", "start", "--web"], cwd: QWENCLAW_DIR, env: { QWENCLAW_AUTO_START: "true", }, }; log(" āœ“ Added MCP server: qwenclaw", "green"); // Save settings try { writeFile(QWEN_SETTINGS_FILE, JSON.stringify(settings, null, 2) + "\n", "utf-8"); log("āœ… Qwen Code settings saved", "green"); } catch (err) { log(`āŒ Error saving settings: ${err.message}`, "red"); } } /** * Create MCP config file */ function createMCPConfig() { log("\nšŸ“ Creating MCP configuration...", "cyan"); const mcpConfig = { mcpServers: { qwenclaw: { command: "bun", args: ["run", "start", "--web"], cwd: QWENCLAW_DIR, env: { QWENCLAW_AUTO_START: "true", }, }, }, }; try { writeFile(QWEN_MCP_FILE, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8"); log("āœ… MCP configuration created: " + QWEN_MCP_FILE, "green"); } catch (err) { log(`āš ļø Could not create MCP config: ${err.message}`, "yellow"); } } /** * Copy startup hook */ function installStartupHook() { log("\nšŸŖ Installing startup hook...", "cyan"); const startupHookSrc = join(QWENCLAW_DIR, "hooks", "qwen-code-startup.js"); const startupHookDest = join(QWEN_DIR, "hooks", "startup.js"); if (existsSync(startupHookSrc)) { try { const hookDir = join(QWEN_DIR, "hooks"); if (!existsSync(hookDir)) { mkdirSync(hookDir, { recursive: true }); } const hookContent = readFileSync(startupHookSrc, "utf-8"); writeFile(startupHookDest, hookContent, "utf-8"); log("āœ… Startup hook installed", "green"); } catch (err) { log(`āš ļø Could not install startup hook: ${err.message}`, "yellow"); } } else { log("āš ļø Startup hook not found", "yellow"); } } /** * Start QwenClaw daemon */ function startDaemon() { log("\nšŸš€ Starting QwenClaw daemon...", "cyan"); const daemonProcess = spawn("bun", ["run", "start", "--web"], { cwd: QWENCLAW_DIR, detached: true, stdio: "ignore", windowsHide: true, }); daemonProcess.unref(); // Wait for daemon to start setTimeout(() => { log("āœ… QwenClaw daemon started", "green"); log(" Web Dashboard: http://127.0.0.1:4632", "cyan"); }, 3000); } /** * Show completion message */ function showCompletion() { log("\n" + "═".repeat(60), "cyan"); log(" āœ… QWENCLAW INSTALLATION COMPLETE!", "green"); log("═".repeat(60), "cyan"); log("\nšŸ“Œ Configuration Summary:", "yellow"); log(" • QwenClaw Directory: " + QWENCLAW_DIR, "cyan"); log(" • Qwen Settings: " + QWEN_SETTINGS_FILE, "cyan"); log(" • MCP Config: " + QWEN_MCP_FILE, "cyan"); log(" • Default Agent: qwenclaw", "cyan"); log(" • Default Skill: qwenclaw-integration", "cyan"); log("\nšŸŽÆ Next Steps:", "yellow"); log(" 1. Restart Qwen Code", "cyan"); log(" 2. QwenClaw will auto-start automatically", "cyan"); log(" 3. Use /qwenclaw commands in Qwen Code chat", "cyan"); log("\nšŸ“š Quick Reference:", "yellow"); log(" /qwenclaw start - Start daemon", "cyan"); log(" /qwenclaw status - Check status", "cyan"); log(" /qwenclaw send - Send message", "cyan"); log(" /qwenclaw skills - List skills", "cyan"); log(" /qwenclaw help - Show help", "cyan"); log("\n🌐 Web Dashboard: http://127.0.0.1:4632", "cyan"); log("\nšŸ“– Documentation: " + QWENCLAW_DIR + "/README.md", "cyan"); log("\n"); } /** * Main installation function */ async function main() { showBanner(); try { checkInstallation(); ensureQwenDir(); updateSettings(); createMCPConfig(); installStartupHook(); startDaemon(); showCompletion(); } catch (err) { log(`\nāŒ Installation failed: ${err.message}`, "red"); log("\nPlease check the error above and try again.", "yellow"); process.exit(1); } } // Run installer main();