feat(openclaw): persist config as ~/.openclaw/openclaw.json

- Read gateway token/port via JSON.parse (same structure as former YAML)
- Update claw.cutos.ai origin by replacing URLs in JSON string fields
- Export resolveOpenclawConfigFile() for frpc + client
- Drop js-yaml dependency
- install: ExecStartPre touches openvfd only if nodes exist (3528-friendly)
- Add tools/deploy-rsync.sh for syncing /opt/clawd over SSH

Made-with: Cursor
This commit is contained in:
stswangzhiping
2026-03-27 13:49:23 +08:00
parent 85026cdf47
commit 884b0dc50a
7 changed files with 111 additions and 46 deletions

View File

@@ -6,7 +6,7 @@ const config = require('./config');
const log = require('./logger');
const { getBoxId } = require('./fingerprint');
const { collect } = require('./metrics');
const { getDashboardInfo, startTtyd, FrpcManager } = require('./frpc'); // getDashboardInfo 也用于心跳中定期刷新
const { getDashboardInfo, resolveOpenclawConfigFile, startTtyd, FrpcManager } = require('./frpc'); // getDashboardInfo 也用于心跳中定期刷新
const { ProvisionManager } = require('./provisioning');
const { BtMonitor } = require('./bt-monitor');
const { hasInternet, getLocalIps } = require('./network');
@@ -407,30 +407,65 @@ class ClawClient {
// ── OpenClaw 配置 ────────────────────────────────────────────────────────────
_updateOpenClawOrigin(targetId) {
const { existsSync, readFileSync, writeFileSync } = require('fs');
const configFile = '/home/sts/.openclaw/config/config.yaml';
const { readFileSync, writeFileSync } = require('fs');
const configFile = resolveOpenclawConfigFile();
if (!existsSync(configFile)) {
log.warn('clawd', `openclaw 配置文件不存在: ${configFile}`);
if (!configFile) {
log.warn('clawd', 'openclaw 配置文件不存在~/.openclaw/openclaw.json 等候选路径均未找到)');
return;
}
try {
const content = readFileSync(configFile, 'utf8');
const raw = readFileSync(configFile, 'utf8');
const config = JSON.parse(raw);
const newOrigin = `https://${targetId}.claw.cutos.ai`;
const re = /https:\/\/[^"'\s]+\.claw\.cutos\.ai/g;
// 替换 https://任意子域.claw.cutos.ai 为目标域名
const updated = content.replace(
/https:\/\/[^"'\s]+\.claw\.cutos\.ai/g,
newOrigin
);
/** 与原 YAML 全文替换等价:遍历 JSON 内所有字符串并替换匹配的 origin */
const replaceOriginStrings = (node) => {
let changed = false;
if (typeof node === 'string') {
return false;
}
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
const v = node[i];
if (typeof v === 'string') {
const next = v.replace(re, newOrigin);
if (next !== v) {
node[i] = next;
changed = true;
}
} else if (v && typeof v === 'object') {
changed = replaceOriginStrings(v) || changed;
}
}
return changed;
}
if (node && typeof node === 'object') {
for (const k of Object.keys(node)) {
const v = node[k];
if (typeof v === 'string') {
const next = v.replace(re, newOrigin);
if (next !== v) {
node[k] = next;
changed = true;
}
} else if (v && typeof v === 'object') {
changed = replaceOriginStrings(v) || changed;
}
}
return changed;
}
return false;
};
if (updated === content) {
if (!replaceOriginStrings(config)) {
log.info('clawd', `openclaw origin 已是 ${newOrigin},无需变更`);
return;
}
writeFileSync(configFile, updated, 'utf8');
writeFileSync(configFile, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
log.info('clawd', `openclaw config 已更新: ${newOrigin}`);
// 文件有变化kill -9 openclaw-gateway让它被 systemd --user 自动拉起

View File

@@ -18,24 +18,36 @@ const FRP_VERSION = '0.62.0';
const TTYD_VERSION = '1.7.7';
const TTYD_PORT = 7681;
/** openclaw 持久化配置JSON结构与原 YAML 解析结果一致。 */
const OPENCLAW_JSON_CANDIDATES = [
path.join(os.homedir(), '.openclaw', 'openclaw.json'),
'/home/sts/.openclaw/openclaw.json',
'/root/.openclaw/openclaw.json',
];
/**
* 解析到第一个存在的 openclaw.json 路径(与 dashboard / 联动更新共用)。
*/
function resolveOpenclawConfigFile() {
for (const p of OPENCLAW_JSON_CANDIDATES) {
try {
if (fs.existsSync(p)) return p;
} catch (_) { /* ignore */ }
}
return null;
}
/**
* 从 openclaw 配置文件中提取 dashboard token 和端口。
* openclaw 将配置持久化存储在 ~/.openclaw/config/config.yaml 中,
* openclaw 将配置持久化存储在 ~/.openclaw/openclaw.json 中,
* 直接读取比执行命令更可靠(不依赖 PATH、不需要进程启动等待
* systemd 服务的 ProtectHome=read-only 允许读取 /home 下的文件。
*/
function getDashboardInfo() {
const yaml = require('js-yaml');
const configCandidates = [
path.join(os.homedir(), '.openclaw', 'config', 'config.yaml'),
'/home/sts/.openclaw/config/config.yaml',
'/root/.openclaw/config/config.yaml',
];
for (const cfgPath of configCandidates) {
for (const cfgPath of OPENCLAW_JSON_CANDIDATES) {
try {
const raw = fs.readFileSync(cfgPath, 'utf8');
const config = yaml.load(raw);
const config = JSON.parse(raw);
const token = config?.gateway?.auth?.token;
const port = config?.gateway?.port || 18789;
if (token) {
@@ -206,4 +218,4 @@ class FrpcManager {
}
}
module.exports = { getDashboardInfo, startTtyd, FrpcManager };
module.exports = { getDashboardInfo, resolveOpenclawConfigFile, startTtyd, FrpcManager };