v1.5.1: Set QwenClaw as Default Agent for Qwen Code
This commit is contained in:
122
.qwen/mcp.json
Normal file
122
.qwen/mcp.json
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# QwenClaw MCP Server Configuration
|
||||||
|
|
||||||
|
This directory contains MCP (Model Context Protocol) server configurations for integrating QwenClaw with Qwen Code and other AI assistants.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. Copy MCP Config to Qwen Code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Windows (PowerShell)
|
||||||
|
Copy-Item .qwen\mcp.json $env:USERPROFILE\.qwen\mcp.json -Force
|
||||||
|
|
||||||
|
# Linux/macOS
|
||||||
|
cp .qwen/mcp.json ~/.qwen/mcp.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Restart Qwen Code
|
||||||
|
|
||||||
|
Close and reopen Qwen Code for the changes to take effect.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### mcp.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"qwenclaw": {
|
||||||
|
"command": "bun",
|
||||||
|
"args": ["run", "start", "--web"],
|
||||||
|
"cwd": "~/qwenclaw",
|
||||||
|
"env": {
|
||||||
|
"QWENCLAW_AUTO_START": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
| Setting | Description | Default |
|
||||||
|
|---------|-------------|---------|
|
||||||
|
| `command` | Command to run QwenClaw | `bun` |
|
||||||
|
| `args` | Arguments for the command | `["run", "start", "--web"]` |
|
||||||
|
| `cwd` | Working directory | `~/qwenclaw` |
|
||||||
|
| `env.QWENCLAW_AUTO_START` | Auto-start daemon | `true` |
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once configured, QwenClaw will:
|
||||||
|
|
||||||
|
1. **Auto-start** when Qwen Code launches
|
||||||
|
2. **Set as default agent** for all interactions
|
||||||
|
3. **Enable skills** automatically:
|
||||||
|
- qwenclaw-integration
|
||||||
|
- gui-automation
|
||||||
|
- qwenbot-integration
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Check if QwenClaw MCP server is connected:
|
||||||
|
|
||||||
|
```
|
||||||
|
/qwenclaw status
|
||||||
|
```
|
||||||
|
|
||||||
|
Should show:
|
||||||
|
```
|
||||||
|
✅ QwenClaw daemon is running
|
||||||
|
PID: 12345
|
||||||
|
Web UI: http://127.0.0.1:4632
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Issue: "MCP server not found"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Verify config location
|
||||||
|
ls ~/.qwen/mcp.json
|
||||||
|
|
||||||
|
# Check QwenClaw installation
|
||||||
|
ls ~/qwenclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: "Failed to start daemon"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Check if port is in use
|
||||||
|
lsof -i :4632
|
||||||
|
|
||||||
|
# Manually start QwenClaw
|
||||||
|
cd ~/qwenclaw
|
||||||
|
bun run start --web
|
||||||
|
```
|
||||||
|
|
||||||
|
### Issue: "Skills not enabled"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
```bash
|
||||||
|
# Update Qwen Code settings
|
||||||
|
# Add to ~/.qwen/settings.json:
|
||||||
|
{
|
||||||
|
"skills": {
|
||||||
|
"enabled": [
|
||||||
|
"qwenclaw-integration",
|
||||||
|
"gui-automation",
|
||||||
|
"qwenbot-integration"
|
||||||
|
],
|
||||||
|
"default": "qwenclaw-integration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- **QwenClaw Docs:** `README.md` in qwenclaw directory
|
||||||
|
- **MCP Protocol:** https://modelcontextprotocol.io/
|
||||||
|
- **Qwen Code:** https://github.com/QwenLM/Qwen-Code
|
||||||
59
.qwen/settings.example.json
Normal file
59
.qwen/settings.example.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"playwright-vision": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"@playwright/mcp@latest",
|
||||||
|
"--caps=vision"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwenclaw": {
|
||||||
|
"command": "bun",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"start",
|
||||||
|
"--web"
|
||||||
|
],
|
||||||
|
"cwd": "~/qwenclaw",
|
||||||
|
"env": {
|
||||||
|
"QWENCLAW_AUTO_START": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"auth": {
|
||||||
|
"selectedType": "qwen-oauth"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$version": 3,
|
||||||
|
"context": {
|
||||||
|
"loadMemoryFromIncludeDirectories": true
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"approvalMode": "yolo"
|
||||||
|
},
|
||||||
|
"ui": {
|
||||||
|
"feedbackLastShownTimestamp": 1769071256658
|
||||||
|
},
|
||||||
|
"skills": {
|
||||||
|
"enabled": [
|
||||||
|
"qwenclaw-integration",
|
||||||
|
"gui-automation",
|
||||||
|
"qwenbot-integration"
|
||||||
|
],
|
||||||
|
"default": "qwenclaw-integration"
|
||||||
|
},
|
||||||
|
"agents": {
|
||||||
|
"default": "qwenclaw",
|
||||||
|
"qwenclaw": {
|
||||||
|
"name": "QwenClaw",
|
||||||
|
"description": "Persistent AI assistant daemon with automation capabilities",
|
||||||
|
"autoStart": true,
|
||||||
|
"skills": [
|
||||||
|
"qwenclaw-integration",
|
||||||
|
"gui-automation",
|
||||||
|
"qwenbot-integration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
README.md
15
README.md
@@ -480,6 +480,21 @@ rm -rf QwenClaw-with-Auth
|
|||||||
|
|
||||||
QwenClaw follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH).
|
QwenClaw follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH).
|
||||||
|
|
||||||
|
### [1.5.1] - 2026-02-26
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
- **QwenClaw as Default Agent** - Auto-start with Qwen Code:
|
||||||
|
- MCP server configuration (`.qwen/mcp.json`)
|
||||||
|
- Startup hook (`hooks/qwen-code-startup.js`)
|
||||||
|
- Auto-configuration script (`scripts/install-qwenclaw.js`)
|
||||||
|
- Default agent settings in Qwen Code
|
||||||
|
- Skills auto-enabled on install
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
- Updated package.json with `setup` script
|
||||||
|
- Added `install-qwenclaw` binary command
|
||||||
|
- Updated version to 1.5.1
|
||||||
|
|
||||||
### [1.5.0] - 2026-02-26
|
### [1.5.0] - 2026-02-26
|
||||||
|
|
||||||
#### Added
|
#### Added
|
||||||
|
|||||||
204
hooks/qwen-code-startup.js
Normal file
204
hooks/qwen-code-startup.js
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Qwen Code Startup Hook for QwenClaw
|
||||||
|
*
|
||||||
|
* This script is automatically executed when Qwen Code starts.
|
||||||
|
* It ensures QwenClaw daemon is running and sets it as the default agent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { spawn } from "child_process";
|
||||||
|
import { join } from "path";
|
||||||
|
import { existsSync, readFileSync, writeFile } 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 QWENCLAW_PID_FILE = join(QWEN_DIR, "qwenclaw", "daemon.pid");
|
||||||
|
|
||||||
|
const COLORS = {
|
||||||
|
cyan: "\x1b[36m",
|
||||||
|
green: "\x1b[32m",
|
||||||
|
yellow: "\x1b[33m",
|
||||||
|
red: "\x1b[31m",
|
||||||
|
reset: "\x1b[0m",
|
||||||
|
};
|
||||||
|
|
||||||
|
function log(message, color = "reset") {
|
||||||
|
console.log(`${COLORS[color]}${message}${COLORS.reset}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if QwenClaw daemon is running
|
||||||
|
*/
|
||||||
|
function isQwenClawRunning() {
|
||||||
|
if (!existsSync(QWENCLAW_PID_FILE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pid = parseInt(readFileSync(QWENCLAW_PID_FILE, "utf-8").trim(), 10);
|
||||||
|
if (isNaN(pid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if process exists
|
||||||
|
process.kill(pid, 0);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start QwenClaw daemon in background
|
||||||
|
*/
|
||||||
|
function startQwenClaw() {
|
||||||
|
log("\n🐾 QwenClaw: Starting daemon...", "cyan");
|
||||||
|
|
||||||
|
const qwenclawProcess = spawn("bun", ["run", "start", "--web"], {
|
||||||
|
cwd: QWENCLAW_DIR,
|
||||||
|
detached: true,
|
||||||
|
stdio: "ignore",
|
||||||
|
windowsHide: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
qwenclawProcess.unref();
|
||||||
|
|
||||||
|
log("✅ QwenClaw daemon started in background", "green");
|
||||||
|
log(" Web Dashboard: http://127.0.0.1:4632", "cyan");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Qwen Code settings to enable QwenClaw as default
|
||||||
|
*/
|
||||||
|
function updateQwenSettings() {
|
||||||
|
log("\n⚙️ QwenClaw: Updating Qwen Code settings...", "cyan");
|
||||||
|
|
||||||
|
let settings = {};
|
||||||
|
|
||||||
|
if (existsSync(QWEN_SETTINGS_FILE)) {
|
||||||
|
try {
|
||||||
|
const content = readFileSync(QWEN_SETTINGS_FILE, "utf-8");
|
||||||
|
settings = JSON.parse(content);
|
||||||
|
} catch (err) {
|
||||||
|
log(`⚠️ Warning: Could not parse settings.json: ${err.message}`, "yellow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable QwenClaw 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set QwenClaw as default skill
|
||||||
|
settings.skills.default = "qwenclaw-integration";
|
||||||
|
|
||||||
|
// Set QwenClaw as default agent
|
||||||
|
if (!settings.agents) {
|
||||||
|
settings.agents = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.agents.default = "qwenclaw";
|
||||||
|
|
||||||
|
if (!settings.agents.qwenclaw) {
|
||||||
|
settings.agents.qwenclaw = {
|
||||||
|
name: "QwenClaw",
|
||||||
|
description: "Persistent AI assistant daemon with automation capabilities",
|
||||||
|
autoStart: true,
|
||||||
|
skills: ["qwenclaw-integration", "gui-automation", "qwenbot-integration"],
|
||||||
|
};
|
||||||
|
log(" + Created default agent: qwenclaw", "green");
|
||||||
|
} else {
|
||||||
|
settings.agents.qwenclaw.autoStart = true;
|
||||||
|
log(" ✓ Updated agent: qwenclaw", "green");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add QwenClaw MCP server
|
||||||
|
if (!settings.mcpServers) {
|
||||||
|
settings.mcpServers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.mcpServers.qwenclaw) {
|
||||||
|
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 updated", "green");
|
||||||
|
} catch (err) {
|
||||||
|
log(`❌ Error saving settings: ${err.message}`, "red");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main startup hook
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
log("\n" + "═".repeat(50), "cyan");
|
||||||
|
log(" 🐾 QWENCLAW - Qwen Code Startup Hook", "cyan");
|
||||||
|
log("═".repeat(50), "cyan");
|
||||||
|
|
||||||
|
// Check if QwenClaw is installed
|
||||||
|
if (!existsSync(QWENCLAW_DIR)) {
|
||||||
|
log("\n❌ QwenClaw not found at: " + QWENCLAW_DIR, "red");
|
||||||
|
log(" Please install QwenClaw first:", "yellow");
|
||||||
|
log(" git clone https://github.rommark.dev/admin/QwenClaw-with-Auth.git", "cyan");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if daemon is running
|
||||||
|
if (isQwenClawRunning()) {
|
||||||
|
log("\n✅ QwenClaw daemon is already running", "green");
|
||||||
|
} else {
|
||||||
|
startQwenClaw();
|
||||||
|
|
||||||
|
// Wait for daemon to start
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Qwen Code settings
|
||||||
|
updateQwenSettings();
|
||||||
|
|
||||||
|
// Show status
|
||||||
|
log("\n" + "═".repeat(50), "cyan");
|
||||||
|
log(" ✅ QwenClaw is ready!", "green");
|
||||||
|
log("═".repeat(50), "cyan");
|
||||||
|
log("\n📌 Quick Commands:", "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("\n🌐 Web Dashboard: http://127.0.0.1:4632", "cyan");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run startup hook
|
||||||
|
main().catch((err) => {
|
||||||
|
log(`\n❌ Startup hook error: ${err.message}`, "red");
|
||||||
|
});
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "qwenclaw",
|
"name": "qwenclaw",
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
"qwenclaw": "./bin/qwenclaw.js"
|
"qwenclaw": "./bin/qwenclaw.js",
|
||||||
|
"install-qwenclaw": "./scripts/install-qwenclaw.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "bun run src/index.ts",
|
"start": "bun run src/index.ts",
|
||||||
@@ -16,7 +17,8 @@
|
|||||||
"start:all": "bun run src/index.ts start --web --with-rig",
|
"start:all": "bun run src/index.ts start --web --with-rig",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"test:rig": "bun test tests/rig-integration.test.ts",
|
"test:rig": "bun test tests/rig-integration.test.ts",
|
||||||
"postinstall": "chmod +x bin/qwenclaw.js 2>/dev/null || true && npx playwright install chromium 2>/dev/null || true"
|
"postinstall": "chmod +x bin/qwenclaw.js 2>/dev/null || true && npx playwright install chromium 2>/dev/null || true",
|
||||||
|
"setup": "bun run scripts/install-qwenclaw.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "^1.3.9"
|
"@types/bun": "^1.3.9"
|
||||||
|
|||||||
313
scripts/install-qwenclaw.js
Normal file
313
scripts/install-qwenclaw.js
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
#!/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();
|
||||||
Reference in New Issue
Block a user