feat: headscale mesh integration - auto-join on bind, logout on unbind

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
stswangzhiping
2026-05-03 19:39:16 +08:00
parent 5e82505c6a
commit 29c158f837
3 changed files with 121 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ const log = require('./logger');
const { getBoxId } = require('./fingerprint');
const { collect } = require('./metrics');
const { getDashboardInfo, resolveOpenclawConfigFile, startTtyd, FrpcManager } = require('./frpc'); // getDashboardInfo 也用于心跳中定期刷新
const headscale = require('./headscale');
const { ProvisionManager } = require('./provisioning');
const { BtMonitor } = require('./bt-monitor');
const { hasInternet, hasWiredInternetProbe, getLocalIps, getLocalNetworks } = require('./network');
@@ -370,16 +371,17 @@ class ClawClient {
_sendConnect() {
const msg = {
type: 'connect',
box_id: this._boxId,
claw_id: this._cfg.claw_id ?? null,
token: this._cfg.token ?? null,
version: CLAWD_VERSION,
ssh_secret_key: this._cfg.ssh_secret_key ?? null,
local_ip: getLocalIps(),
local_networks: getLocalNetworks(),
external_ip: this._externalIp ?? null,
location: this._location ?? null,
type: 'connect',
box_id: this._boxId,
claw_id: this._cfg.claw_id ?? null,
token: this._cfg.token ?? null,
version: CLAWD_VERSION,
ssh_secret_key: this._cfg.ssh_secret_key ?? null,
headscale_joined: headscale.isInstalled() && headscale.isJoined(this._cfg.headscale_server || 'https://hs.claw.cutos.ai'),
local_ip: getLocalIps(),
local_networks: getLocalNetworks(),
external_ip: this._externalIp ?? null,
location: this._location ?? null,
...this._dashInfo,
};
this._send(msg);
@@ -400,6 +402,9 @@ class ClawClient {
case 'upgrade':
this._handleUpgrade(msg);
break;
case 'headscale_logout':
headscale.logout().catch(e => log.error('headscale', 'logout 失败:', e.message));
break;
case 'error':
log.error('clawd', `服务器错误: ${msg.msg}`);
if (msg.msg === 'hardware_mismatch') {
@@ -443,6 +448,18 @@ class ClawClient {
});
}
// headscale收到 authkey 则加入 mesh
if (msg.headscale_authkey && msg.headscale_server) {
const hostname = msg.headscale_hostname || `claw-${msg.claw_id}`;
this._cfg.headscale_server = msg.headscale_server;
config.save(this._cfg);
headscale.join(msg.headscale_server, msg.headscale_authkey, hostname)
.then(ok => {
if (ok) log.info('headscale', `已加入 mesh: ${hostname}`);
})
.catch(e => log.error('headscale', '加入 mesh 失败:', e.message));
}
this._startHeartbeat();
}