Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 10 additions & 55 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,59 +1,14 @@
# FSC Worker Daemon Environment Variables
# Copy this file to .env and fill in your values
# 环境变量配置示例
# 复制此文件为 .env 并填入实际值

# ============ Redis Configuration ============
REDIS_HOST=10.10.0.1
# Redis 配置
REDIS_HOST=100.80.67.125
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password

# ============ Worker Configuration ============
# Unique identifier for this worker instance
AGENT_ID=worker-node1
# LLM API Keys
DOUBAO_API_KEY=your-doubao-api-key
MINIMAX_API_KEY=your-minimax-api-key

# Maximum number of concurrent tasks
MAX_CONCURRENT=10

# ============ WireGuard Configuration ============
# WireGuard interface name
WG_INTERFACE=wg0

# Node IP in the mesh network
NODE_IP=10.10.0.1

# WireGuard private key (generate with: wg genkey)
WG_PRIVATE_KEY=your_private_key_here

# ============ Docker Configuration ============
# Docker network mode
DOCKER_NETWORK_MODE=bridge

# Docker resource limits
DOCKER_MEMORY_LIMIT=2g
DOCKER_CPU_LIMIT=2

# ============ Logging ============
# Log level: debug, info, warn, error
LOG_LEVEL=info

# Log file path
LOG_FILE=fsc-worker.log

# ============ MemoV Configuration ============
# MemoV Git repository path
MEMOV_PATH=/opt/claw-mesh/.mem

# MemoV branch for this agent
MEMOV_BRANCH=agent-${AGENT_ID}

# ============ Task Configuration ============
# Maximum retry attempts for failed tasks
RETRY_ATTEMPTS=3

# Task timeout in seconds
TASK_TIMEOUT=300

# ============ Health Check ============
# Health check interval in milliseconds
HEALTH_CHECK_INTERVAL=30000

# Health check key in Redis
HEALTH_CHECK_KEY=fsc:worker:health
# Memos 集成
MEMOS_TOKEN=your-memos-token
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,19 @@ package-lock.json
bun.lockb
*.so
*.dylib

# 敏感文件
*.key
*.pem
wg0.conf
.memov.pid
.claw-mesh.pid

# 本地数据
redis_data/
qdrant_storage/
.mem/

# 构建产物
bun.lock
packages/
55 changes: 55 additions & 0 deletions TAILSCALE-INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Tailscale 网络融合完成

## 变更内容

### 1. 新增网络配置模块 (`config/network.ts`)
- 定义节点拓扑(Mac 本地、硅谷、Windows、东京)
- Tailscale IP 映射(100.x.x.x)
- 节点角色管理(master/worker)
- 动态节点发现和在线检测

### 2. 更新 Redis 配置 (`config/redis.ts`)
- 自动选择 Redis 主节点(优先硅谷节点 100.80.67.125)
- 支持本地 fallback(127.0.0.1)

### 3. 更新 MemoV Sync Daemon (`fsc/memov-sync-daemon.ts`)
- 动态获取活跃节点列表
- 自动同步到 Tailscale 网络节点

## 当前网络状态

```
✅ Mac 本地 (master) - 100.114.56.105
✅ 硅谷节点 (worker) - 100.80.67.125 [在线]
❌ Windows 节点 (worker) - 100.101.173.35 [已禁用]
❌ 东京节点 (worker) - 未配置
```

## Redis 连接
- 主节点: 100.80.67.125:6379 (硅谷)
- 状态: 已连接

## MemoV 状态
- 进程 ID: 93036
- 同步节点: 100.80.67.125
- 状态: 正在监听事件流

## 添加新节点步骤

1. 确保新节点已加入 Tailscale 网络
2. 编辑 `config/network.ts`,添加节点配置
3. 设置 `enabled: true`
4. 重启 memov: `kill $(cat .memov.pid) && bun run fsc/memov-sync-daemon.ts > logs/memov-sync.log 2>&1 &`

## 测试命令

```bash
# 查看网络配置
bun run test-network.ts

# 查看 memov 日志
tail -f logs/memov-sync.log

# 检查进程
ps -p $(cat .memov.pid)
```
102 changes: 102 additions & 0 deletions config/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* 网络配置 — Tailscale 节点映射
*
* 原架构使用 WireGuard (10.10.0.x),现改用 Tailscale (100.x.x.x)
* 保持逻辑节点 ID 不变,映射到 Tailscale IP
*/

export type NodeId = 'mac-local' | 'silicon-valley' | 'windows' | 'tokyo';

export interface NodeConfig {
id: NodeId;
name: string;
tailscaleIp: string;
wireguardIp?: string; // 保留用于兼容
role: 'master' | 'worker';
enabled: boolean;
}

export const NODES: Record<NodeId, NodeConfig> = {
'mac-local': {
id: 'mac-local',
name: 'lunnymacbook-pro',
tailscaleIp: '100.114.56.105',
wireguardIp: '10.10.0.5',
role: 'master',
enabled: true,
},
'silicon-valley': {
id: 'silicon-valley',
name: 'vm-0-6-debian',
tailscaleIp: '100.80.67.125',
wireguardIp: '10.10.0.2',
role: 'worker',
enabled: true,
},
'windows': {
id: 'windows',
name: 'win-taq1rm10mnf',
tailscaleIp: '100.101.173.35',
wireguardIp: '10.10.0.4',
role: 'worker',
enabled: true, // 已启用
},
'tokyo': {
id: 'tokyo',
name: 'tokyo-node',
tailscaleIp: '', // 待添加
wireguardIp: '10.10.0.3',
role: 'worker',
enabled: false,
},
};

/**
* 获取所有启用的节点 IP(排除本机)
*/
export function getActiveNodeIps(excludeLocal = true): string[] {
return Object.values(NODES)
.filter(node => node.enabled)
.filter(node => !excludeLocal || node.id !== 'mac-local')
.map(node => node.tailscaleIp)
.filter(ip => ip !== '');
}

/**
* 获取 Redis 主节点 IP
* 优先使用硅谷节点,fallback 到本地
*/
export function getRedisMasterIp(): string {
const sv = NODES['silicon-valley'];
if (sv.enabled && sv.tailscaleIp) {
return sv.tailscaleIp;
}
return '127.0.0.1'; // 本地 fallback
}

/**
* 根据节点 ID 获取 IP
*/
export function getNodeIp(nodeId: NodeId): string | null {
const node = NODES[nodeId];
return node?.enabled ? node.tailscaleIp : null;
}

/**
* 检查节点是否在线(通过 Tailscale)
*/
export async function checkNodeOnline(nodeId: NodeId): Promise<boolean> {
const ip = getNodeIp(nodeId);
if (!ip) return false;

try {
const proc = Bun.spawn(['ping', '-c', '1', '-W', '2', ip], {
stdout: 'pipe',
stderr: 'pipe',
});
const exitCode = await proc.exited;
return exitCode === 0;
} catch {
return false;
}
}
7 changes: 5 additions & 2 deletions config/redis.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/**
* Redis 连接配置 — 集中管理,避免 6 处硬编码
* v2: 支持 Tailscale 网络
*/
export const REDIS_HOST = process.env.REDIS_HOST || '10.10.0.1';
import { getRedisMasterIp } from './network';

export const REDIS_HOST = process.env.REDIS_HOST || getRedisMasterIp();
export const REDIS_PORT = parseInt(process.env.REDIS_PORT || '6379');
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || 'fsc-mesh-2026';
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || ''; // 从环境变量读取

export const REDIS_URL = `redis://:${REDIS_PASSWORD}@${REDIS_HOST}:${REDIS_PORT}`;

Expand Down
4 changes: 3 additions & 1 deletion fsc/memov-sync-daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import { readFile, writeFile } from 'fs/promises';

// ============ 配置 ============
import { REDIS_HOST, REDIS_PORT, REDIS_PASSWORD } from '../config/redis';
import { getActiveNodeIps } from '../config/network';

const MEM_DIR = '.mem';
const NODES = ['10.10.0.2', '10.10.0.3', '10.10.0.4'];
const NODES = getActiveNodeIps(); // 动态获取 Tailscale 节点
const CONSUMER_GROUP = 'memov-sync';
const CONSUMER_NAME = `memov-${process.env.HOSTNAME || 'local'}`;
const STREAM_KEY = 'fsc:mem_events';
Expand Down
26 changes: 26 additions & 0 deletions test-network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bun
/**
* 测试 Tailscale 网络配置
*/
import { NODES, getActiveNodeIps, getRedisMasterIp, checkNodeOnline } from './config/network';

console.log('📡 Tailscale 网络配置\n');

console.log('所有节点:');
for (const [id, node] of Object.entries(NODES)) {
const status = node.enabled ? '✅' : '❌';
console.log(` ${status} ${node.name} (${id})`);
console.log(` Tailscale: ${node.tailscaleIp || '未配置'}`);
console.log(` 角色: ${node.role}`);
console.log('');
}

console.log('活跃节点 IP:', getActiveNodeIps());
console.log('Redis 主节点:', getRedisMasterIp());

console.log('\n🔍 检查节点在线状态...');
for (const [id, node] of Object.entries(NODES)) {
if (!node.enabled) continue;
const online = await checkNodeOnline(id as any);
console.log(` ${online ? '🟢' : '🔴'} ${node.name}: ${online ? 'online' : 'offline'}`);
}