'use strict'; const fs = require('fs'); const path = require('path'); const os = require('os'); // 生产环境用 /etc/clawd/,开发环境用 ~/.clawd/ const CONFIG_DIR = process.env.CLAWD_CONFIG_DIR || (process.getuid && process.getuid() === 0 ? '/etc/clawd' : path.join(os.homedir(), '.clawd')); const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json'); const DEFAULTS = { server: 'wss://claw.cutos.ai/ws', claw_id: null, token: null, heartbeat_interval: 30, // 秒 /** 云端已激活:用于启动/重连时立即点亮 alarm(pwr),不等首包 connected */ activated: false, ssh_secret_key: null, share_key: null, headscale_server: 'https://hs.claw.cutos.ai', }; function _generateSshSecretKey() { const bytes = require('crypto').randomBytes(16); return 'sk-' + bytes.toString('hex'); } /** * 生成可读性好的 Samba 共享密码,格式:xxxxxx-xxxxxx-xxxxXX(三段各6字符,连字符分隔)。 * 最后一段包含至少1个大写字母和1个数字,其余为小写字母。 * 示例:juqhuf-hykgyh-mykGi3 */ function _generateShareKey() { const crypto = require('crypto'); const lower = 'abcdefghijklmnopqrstuvwxyz'; const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; const digits = '0123456789'; function rndChars(chars, n) { const bytes = crypto.randomBytes(n); return Array.from(bytes).map(b => chars[b % chars.length]).join(''); } const g1 = rndChars(lower, 6); const g2 = rndChars(lower, 6); // 第三段:4个小写 + 1个大写 + 1个数字,随机打乱顺序 const g3raw = (rndChars(lower, 4) + rndChars(upper, 1) + rndChars(digits, 1)).split(''); for (let i = g3raw.length - 1; i > 0; i--) { const j = crypto.randomBytes(1)[0] % (i + 1); [g3raw[i], g3raw[j]] = [g3raw[j], g3raw[i]]; } return `${g1}-${g2}-${g3raw.join('')}`; } function load() { let cfg; try { if (fs.existsSync(CONFIG_FILE)) { const raw = fs.readFileSync(CONFIG_FILE, 'utf8'); cfg = Object.assign({}, DEFAULTS, JSON.parse(raw)); } } catch (e) { const log = require('./logger'); log.error('config', '读取配置失败,使用默认值:', e.message); } if (!cfg) cfg = Object.assign({}, DEFAULTS); let dirty = false; if (!cfg.ssh_secret_key) { cfg.ssh_secret_key = _generateSshSecretKey(); dirty = true; } if (!cfg.share_key) { cfg.share_key = _generateShareKey(); dirty = true; } if (dirty) save(cfg); return cfg; } function save(data) { try { fs.mkdirSync(CONFIG_DIR, { recursive: true }); fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), 'utf8'); } catch (e) { const log = require('./logger'); log.error('config', '写入配置失败:', e.message); } } function getConfigPath() { return CONFIG_FILE; } module.exports = { load, save, getConfigPath };