| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- #!/usr/bin/env node
- /**
- * OpenClaw 全量安装脚本 v4.1
- *
- * 一键安装 Skills + Workshop + Token 工具 + 凭证模板
- *
- * 用法:
- * node install-all.js # 安装全部
- * node install-all.js --dry-run # 预览模式
- * node install-all.js --skills-only # 只装 Skills
- * node install-all.js --workshop-only # 只装 Workshop
- * node install-all.js --list # 列出内容
- */
- const fs = require('fs');
- const path = require('path');
- const os = require('os');
- // ============================================
- // 配置
- // ============================================
- const VERSION = '4.1.0';
- const SRC_DIR = __dirname;
- const HOME = os.homedir();
- const OPENCLAW_DIR = path.join(HOME, '.openclaw');
- const SKILLS_TARGET = path.join(OPENCLAW_DIR, 'skills');
- const WORKSPACE_TARGET = path.join(OPENCLAW_DIR, 'workspace');
- const MEMORY_TARGET = path.join(WORKSPACE_TARGET, 'memory');
- const TOOLS_TARGET = path.join(OPENCLAW_DIR, 'tools');
- const WORKSPACE_TOOLS_TARGET = path.join(WORKSPACE_TARGET, 'scripts', 'tools');
- const CREDENTIALS_PATH = path.join(OPENCLAW_DIR, 'voc-credentials.json');
- // ============================================
- // 参数
- // ============================================
- const args = process.argv.slice(2);
- const DRY_RUN = args.includes('--dry-run');
- const SKILLS_ONLY = args.includes('--skills-only');
- const WORKSHOP_ONLY = args.includes('--workshop-only');
- const LIST_ONLY = args.includes('--list') || args.includes('-l');
- const HELP = args.includes('--help') || args.includes('-h');
- // ============================================
- // 工具函数
- // ============================================
- function ensureDir(dir) {
- if (!DRY_RUN && !fs.existsSync(dir)) {
- fs.mkdirSync(dir, { recursive: true });
- }
- }
- function copyFile(src, dest) {
- if (DRY_RUN) return;
- ensureDir(path.dirname(dest));
- fs.copyFileSync(src, dest);
- }
- function copyDirRecursive(srcDir, destDir) {
- let count = 0;
- if (!fs.existsSync(srcDir)) return count;
- for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
- const src = path.join(srcDir, entry.name);
- const dest = path.join(destDir, entry.name);
- if (entry.isDirectory()) {
- count += copyDirRecursive(src, dest);
- } else if (entry.isFile()) {
- copyFile(src, dest);
- count++;
- }
- }
- return count;
- }
- function formatSize(bytes) {
- if (bytes < 1024) return bytes + ' B';
- if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
- return (bytes / 1024 / 1024).toFixed(1) + ' MB';
- }
- // ============================================
- // Skills 安装
- // ============================================
- function installSkills() {
- const skillsDir = path.join(SRC_DIR, 'skills');
- if (!fs.existsSync(skillsDir)) {
- console.log(' ⚠️ skills/ 目录不存在,跳过');
- return { installed: 0, files: 0 };
- }
- const skills = fs.readdirSync(skillsDir).filter(d => {
- const p = path.join(skillsDir, d);
- return fs.statSync(p).isDirectory() && fs.existsSync(path.join(p, 'SKILL.md'));
- }).sort();
- console.log(` 共 ${skills.length} 个技能 → ${SKILLS_TARGET}`);
- console.log('');
- let fileCount = 0;
- for (const skill of skills) {
- const srcSkillDir = path.join(skillsDir, skill);
- const destSkillDir = path.join(SKILLS_TARGET, skill);
- const files = fs.readdirSync(srcSkillDir).filter(f =>
- fs.statSync(path.join(srcSkillDir, f)).isFile()
- );
- for (const f of files) {
- copyFile(path.join(srcSkillDir, f), path.join(destSkillDir, f));
- fileCount++;
- }
- process.stdout.write(` ✅ ${skill}\n`);
- }
- return { installed: skills.length, files: fileCount };
- }
- function listSkills() {
- const skillsDir = path.join(SRC_DIR, 'skills');
- if (!fs.existsSync(skillsDir)) return;
- const skills = fs.readdirSync(skillsDir).filter(d => {
- const p = path.join(skillsDir, d);
- return fs.statSync(p).isDirectory() && fs.existsSync(path.join(p, 'SKILL.md'));
- }).sort();
- console.log(` Skills (${skills.length}):`);
- for (const s of skills) {
- const files = fs.readdirSync(path.join(skillsDir, s)).filter(f =>
- fs.statSync(path.join(skillsDir, s, f)).isFile()
- );
- const totalSize = files.reduce((sum, f) =>
- sum + fs.statSync(path.join(skillsDir, s, f)).size, 0
- );
- console.log(` ${s.padEnd(38)} ${formatSize(totalSize).padStart(8)} [${files.join(', ')}]`);
- }
- console.log('');
- }
- // ============================================
- // Workshop 安装
- // ============================================
- function installWorkshop() {
- const workshopDir = path.join(SRC_DIR, 'workshop');
- if (!fs.existsSync(workshopDir)) {
- console.log(' ⚠️ workshop/ 目录不存在,跳过');
- return { installed: 0 };
- }
- let count = 0;
- // Playbooks + supporting docs
- const mdFiles = fs.readdirSync(workshopDir).filter(f =>
- f.endsWith('.md') && fs.statSync(path.join(workshopDir, f)).isFile()
- );
- console.log(` Playbooks → ${WORKSPACE_TARGET}`);
- for (const f of mdFiles) {
- copyFile(path.join(workshopDir, f), path.join(WORKSPACE_TARGET, f));
- const size = formatSize(fs.statSync(path.join(workshopDir, f)).size);
- console.log(` ✅ ${f} (${size})`);
- count++;
- }
- console.log('');
- // Memory templates
- const memDir = path.join(workshopDir, 'memory-templates');
- if (fs.existsSync(memDir)) {
- const jsonFiles = fs.readdirSync(memDir).filter(f =>
- f.endsWith('.json') && fs.statSync(path.join(memDir, f)).isFile()
- );
- console.log(` Memory Templates → ${MEMORY_TARGET}`);
- for (const f of jsonFiles) {
- copyFile(path.join(memDir, f), path.join(MEMORY_TARGET, f));
- const size = formatSize(fs.statSync(path.join(memDir, f)).size);
- console.log(` ✅ ${f} (${size})`);
- count++;
- }
- console.log('');
- }
- return { installed: count };
- }
- function listWorkshop() {
- const workshopDir = path.join(SRC_DIR, 'workshop');
- if (!fs.existsSync(workshopDir)) return;
- const mdFiles = fs.readdirSync(workshopDir).filter(f =>
- f.endsWith('.md') && fs.statSync(path.join(workshopDir, f)).isFile()
- );
- console.log(` Workshop Playbooks (${mdFiles.length}):`);
- for (const f of mdFiles) {
- const size = formatSize(fs.statSync(path.join(workshopDir, f)).size);
- console.log(` ${f.padEnd(42)} ${size}`);
- }
- const memDir = path.join(workshopDir, 'memory-templates');
- if (fs.existsSync(memDir)) {
- const jsonFiles = fs.readdirSync(memDir).filter(f =>
- f.endsWith('.json') && fs.statSync(path.join(memDir, f)).isFile()
- );
- console.log(` Memory Templates (${jsonFiles.length}):`);
- for (const f of jsonFiles) {
- const size = formatSize(fs.statSync(path.join(memDir, f)).size);
- console.log(` ${f.padEnd(42)} ${size}`);
- }
- }
- console.log('');
- }
- // ============================================
- // 工具安装
- // ============================================
- function installTools() {
- let count = 0;
- const packagedToolsDir = path.join(SRC_DIR, 'tools');
- if (fs.existsSync(packagedToolsDir)) {
- count += copyDirRecursive(packagedToolsDir, TOOLS_TARGET);
- const mirrorCount = copyDirRecursive(packagedToolsDir, WORKSPACE_TOOLS_TARGET);
- console.log(` ✅ tools/* → ${TOOLS_TARGET} (${count} files)`);
- console.log(` ✅ tools/* → ${WORKSPACE_TOOLS_TARGET} (${mirrorCount} files)`);
- return count;
- }
- const legacyTools = ['set-voc-token.js', 'voc-token-preflight.js'];
- for (const name of legacyTools) {
- const src = path.join(SRC_DIR, name);
- if (!fs.existsSync(src)) {
- console.log(` ⚠️ ${name} 不存在,跳过`);
- continue;
- }
- copyFile(src, path.join(TOOLS_TARGET, name));
- copyFile(src, path.join(WORKSPACE_TOOLS_TARGET, name));
- console.log(` ✅ ${name}`);
- count++;
- }
- return count;
- }
- function installCredentialsTemplate() {
- if (fs.existsSync(CREDENTIALS_PATH)) {
- // 不覆盖已有的凭证文件
- console.log(` ⏭️ ${CREDENTIALS_PATH} 已存在,跳过`);
- return;
- }
- const template = {
- _comment: "VOC-AI Token 配置文件。充值登录后将获得的 session token 填入 vocToken 字段。",
- _format: "Token 格式为 r:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(32位十六进制)",
- _setup: "运行 node ~/.openclaw/tools/set-voc-token.js <session-token> 自动写入",
- _payment: "充值页面: https://app.fmode.cn/dev/apig-pay/?apigid=Vo3ROWEvDy&fun_id=HOkkX72PMF",
- vocToken: ""
- };
- if (!DRY_RUN) {
- ensureDir(OPENCLAW_DIR);
- fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(template, null, 2) + '\n', 'utf-8');
- }
- console.log(` ✅ 凭证模板 → ${CREDENTIALS_PATH}`);
- }
- // ============================================
- // Help
- // ============================================
- function showHelp() {
- console.log(`
- OpenClaw 全量安装脚本 v${VERSION}
- Usage:
- node install-all.js [options]
- Options:
- --list, -l 列出包含内容
- --dry-run 预览模式
- --skills-only 只安装 Skills
- --workshop-only 只安装 Workshop
- --help, -h 显示帮助
- 安装内容:
- Skills → ~/.openclaw/skills/ (技能文件)
- Workshop → ~/.openclaw/workspace/ (工作坊 playbooks)
- Memory → ~/.openclaw/workspace/memory/(记忆模板)
- Tools → ~/.openclaw/tools/ (本地脚本工具)
- Tool mirror → ~/.openclaw/workspace/scripts/tools/ (Skill 相对路径执行)
- Credentials → ~/.openclaw/voc-credentials.json (Token 配置模板)
- Token 设置流程:
- 1. 打开充值页面: https://app.fmode.cn/dev/apig-pay/?apigid=Vo3ROWEvDy&fun_id=HOkkX72PMF
- 2. 登录并充值
- 3. 获取 session token (格式: r:xxxx...)
- 4. 运行: node ~/.openclaw/tools/set-voc-token.js <your-token>
- 5. 重启 OpenClaw gateway
- `);
- }
- // ============================================
- // Main
- // ============================================
- function main() {
- if (HELP) { showHelp(); return; }
- console.log('');
- console.log('╔══════════════════════════════════════════════════╗');
- console.log(`║ OpenClaw 全量安装脚本 v${VERSION} ║`);
- console.log('╚══════════════════════════════════════════════════╝');
- if (DRY_RUN) console.log(' 📌 预览模式');
- console.log('');
- // ── List mode ──
- if (LIST_ONLY) {
- if (!WORKSHOP_ONLY) listSkills();
- if (!SKILLS_ONLY) listWorkshop();
- return;
- }
- // ── Install ──
- let totalSkills = 0;
- let totalFiles = 0;
- let totalWorkshop = 0;
- let totalTools = 0;
- // Tools + Credentials(总是安装)
- console.log('── Token 工具 + 凭证 ──');
- totalTools = installTools();
- installCredentialsTemplate();
- console.log('');
- // Skills
- if (!WORKSHOP_ONLY) {
- console.log('── Skills ──');
- const sr = installSkills();
- totalSkills = sr.installed;
- totalFiles = sr.files;
- console.log('');
- }
- // Workshop
- if (!SKILLS_ONLY) {
- console.log('── Workshop ──');
- const wr = installWorkshop();
- totalWorkshop = wr.installed;
- }
- // Summary
- console.log('══════════════════════════════════════════════════');
- const prefix = DRY_RUN ? '[预览] ' : '';
- console.log(`${prefix}安装完成!`);
- if (!WORKSHOP_ONLY) console.log(` 🔧 Skills: ${totalSkills} 个技能 (${totalFiles} 个文件) → ${SKILLS_TARGET}`);
- if (!SKILLS_ONLY) console.log(` 🦐 Workshop: ${totalWorkshop} 个文件 → ${WORKSPACE_TARGET}`);
- console.log(` 🔑 Tools: ${totalTools} 个文件 → ${TOOLS_TARGET}`);
- console.log(` 🧰 Tool mirror → ${WORKSPACE_TOOLS_TARGET}`);
- console.log(` 📄 凭证配置 → ${CREDENTIALS_PATH}`);
- console.log('');
- if (!DRY_RUN) {
- console.log('📋 下一步:');
- console.log(' 1. 设置 Token(如果还没有):');
- console.log(' 打开 https://app.fmode.cn/dev/apig-pay/?apigid=Vo3ROWEvDy&fun_id=HOkkX72PMF');
- console.log(' 登录充值后获取 session token,运行:');
- console.log(' node ~/.openclaw/tools/set-voc-token.js <your-session-token>');
- console.log('');
- console.log(' 2. 重启 OpenClaw gateway 加载新文件');
- console.log('');
- }
- }
- main();
|