eisonme-node-transfer

TotalClaw 作者 totalclaw

OpenClaw 节点间基于 Node.js 流的高速大文件传输,避免 Base64 与 OOM。

安装 / 下载方式

TotalClaw CLI推荐
totalclaw skills install shop:totalclaw:totalclaw~eisonme-node-transfer
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/totalclaw%3Atotalclaw~eisonme-node-transfer/file -o eisonme-node-transfer.md
## 概述(中文)

OpenClaw 节点间基于 Node.js 流的高速大文件传输,避免 Base64 与 OOM。

## 技能正文

# node-transfer(节点传输)

使用原生 Node.js 流在 OpenClaw 节点间进行高速、内存高效的文件传输。

## 📋 目录

- [解决的问题](#problem-solved)
- [架构](#architecture)
- [要求](#requirements)
- [安装](#installation)
- [用法](#usage)
- [API 参考](#api-reference)
- [故障排除](#troubleshooting)

---

## 🎯 解决的问题

### 原始问题

使用标准 `nodes.invoke` 机制在 OpenClaw 节点间传输大文件时,我们遇到几个关键问题:

| 问题 | 影响 |
|-------|--------|
| **Base64 编码开销** | 载荷大 33%,传输更慢 |
| **内存耗尽 (OOM)** | 将数 GB 文件载入内存导致进程崩溃 |
| **传输延迟** | JSON 序列化/反序列化增加显著延迟 |
| **9 分钟部署** | 每次传输都重新部署脚本 |

### 解决方案

`node-transfer` 使用 **原生 HTTP 流**与 Node.js 流,提供:

- ✅ **零内存开销** — 文件从磁盘直接流到网络
- ✅ **无 Base64 编码** — 原始二进制传输
- ✅ **速度** — 仅受网络带宽限制的线速
- ✅ **一次安装,多次运行** — 首次部署后脚本持久保留在节点

### 性能对比

| 指标 | Base64 传输 | node-transfer | 提升 |
|--------|----------------|---------------|-------------|
| 1GB 文件传输时间 | ~15-30 分钟 | ~8 秒 | **约 150 倍** |
| 内存使用 | 1GB+ | <10MB | **减少 99%** |
| 首次传输开销 | N/A | ~30 秒(一次性安装) | - |
| 后续传输 | ~15-30 分钟 | **<1 秒** 检查 + ~8 秒传输 | **约 200 倍** |

---

## 🏗️ 架构

### 工作原理

```
┌──────────────┐     HTTP Stream      ┌──────────────┐
│  send.js     │ ◄──────────────────► │ receive.js   │
│  (Source)    │   (Token-protected)  │ (Destination)│
└──────────────┘                      └──────────────┘
       │                                     │
       ▼                                     ▼
┌──────────────┐                      ┌──────────────┐
│  Read Stream │                      │ Write Stream │
│  (fs.create  │                      │ (fs.create   │
│   ReadStream)│                      │  WriteStream)│
└──────────────┘                      └──────────────┘
       │                                     │
       ▼                                     ▼
┌──────────────┐                      ┌──────────────┐
│  File on     │                      │  File on     │
│  Disk        │                      │  Disk        │
└──────────────┘                      └──────────────┘
```

### 安全模型

1. **一次性令牌**:256 位加密随机令牌(64 个十六进制字符)
2. **单次连接**:每个令牌仅允许一次下载
3. **自动关闭**:传输完成或断开后服务器关闭
4. **令牌验证**:每个请求必须包含正确令牌

### 数据流

1. **发送方**(`send.js`):
   - 生成随机端口与安全令牌
   - 在临时端口启动 HTTP 服务器
   - 将文件从磁盘直接流到 HTTP 响应
   - 传输或超时后自动关闭(默认 5 分钟)

2. **接收方**(`receive.js`):
   - 使用令牌连接发送方 URL
   - 将 HTTP 响应直接流到磁盘
   - 报告进度、速度与完成状态
   - 验证接收字节与预期大小一致

---

## 📦 要求

- **Node.js**:14.0.0 或更高
- **网络**:节点间 TCP 连通(任意端口 1024-65535)
- **防火墙**:必须允许出站连接与临时端口入站
- **磁盘空间**:目标端有足够空间存放接收文件

---

## 🚀 安装

### 「一次安装」模式

不在每次传输时部署脚本,而是**每节点部署一次**,后续传输使用快速版本检查。

### 方法 1:使用 deploy.js(推荐)

```bash
# Generate deployment script for a target node
node deploy.js E3V3

# This outputs a PowerShell script that you can execute via nodes.invoke()
```

### 方法 2:手动部署

在每个目标节点创建目录并复制文件:

```powershell
# Create directory
mkdir C:/openclaw/skills/node-transfer/scripts -Force

# Copy these files (ensure UTF-8 without BOM encoding):
# - send.js
# - receive.js
# - ensure-installed.js
# - version.js
```

### 方法 3:通过 OpenClaw 智能体

```javascript
// 1. Check if already installed (< 100ms)
const check = await nodes.invoke({
    node: 'E3V3',
    command: ['node', 'C:/openclaw/skills/node-transfer/scripts/ensure-installed.js', 
              'C:/openclaw/skills/node-transfer/scripts']
});

const checkResult = JSON.parse(check.output);

if (!checkResult.installed) {
    // 2. Deploy if needed (one-time, ~30 seconds)
    // Use the deploy.js output or manually copy files
    console.log('Deploying node-transfer to E3V3...');
    // ... deployment code ...
}
```

---

## 💡 用法

### 基本传输工作流

```javascript
const INSTALL_DIR = 'C:/openclaw/skills/node-transfer/scripts';
const SOURCE_NODE = 'E3V3';
const DEST_NODE = 'E3V3-Docker';

// Step 1: Check installation on both nodes (fast!)
const [sourceCheck, destCheck] = await Promise.all([
    nodes.invoke({
        node: SOURCE_NODE,
        command: ['node', `${INSTALL_DIR}/ensure-installed.js`, INSTALL_DIR]
    }),
    nodes.invoke({
        node: DEST_NODE,
        command: ['node', `${INSTALL_DIR}/ensure-installed.js`, INSTALL_DIR]
    })
]);

// Deploy if needed (usually only once per node ever)
// ... deployment code if not installed ...

// Step 2: Start sender on source node
const sendResult = await nodes.invoke({
    node: SOURCE_NODE,
    command: ['node', `${INSTALL_DIR}/send.js`, 'C:/data/large-file.zip']
});

const { url, token, fileSize, fileName } = JSON.parse(sendResult.output);

// Step 3: Start receiver on destination node
const receiveResult = await nodes.invoke({
    node: DEST_NODE,
    command: ['node', `${INSTALL_DIR}/receive.js`, url, token, '/incoming/file.zip']
});

const result = JSON.parse(receiveResult.output);
console.log(`Transferred ${result.bytesReceived} bytes in ${result.duration}s at ${result.speedMBps} MB/s`);
```

### 使用命令行

#### 发送方

```bash
node send.js /path/to/file.zip
```

输出:
```json
{
  "url": "http://192.168.1.10:54321/transfer",
  "token": "a1b2c3d4e5f6789...",
  "fileSize": 1073741824,
  "fileName": "file.zip",
  "sourceIp": "192.168.1.10",
  "port": 54321,
  "version": "1.0.0"
}
```

选项:
```bash
node send.js /path/to/file.zip --port 8080 --timeout 10
node send.js --help
node send.js --version
```

#### 接收方

```bash
node receive.js "http://192.168.1.10:54321/transfer" "token-here..." /path/to/save.zip
```

输出:
```json
{
  "success": true,
  "bytesReceived": 1073741824,
  "totalBytes": 1073741824,
  "duration": 8.42,
  "speedMBps": 121.5,
  "outputPath": "/path/to/save.zip"
}
```

选项:
```bash
node receive.js <url> <token> <output> --timeout 60 --no-progress
node receive.js --help
node receive.js --version
```

---

## 📚 API 参考

### send.js

启动 HTTP 服务器以流式传输文件。

**用法:** `node send.js <filePath> [options]`

**参数:**
- `filePath`(必需):要发送的文件路径

**选项:**
- `--port <n>`:使用指定端口(默认:随机临时端口)
- `--timeout <n>`:超时分钟数(默认:5)

**输出 (JSON):**
| 字段 | 类型 | 说明 |
|-------|------|-------------|
| `url` | string | 接收方连接的 HTTP URL |
| `token` | string | 安全令牌(64 个十六进制字符) |
| `fileSize` | number | 文件大小(字节) |
| `fileName` | string | 原始文件名 |
| `sourceIp` | string | 发送方 IP 地址 |
| `port` | number | 使用的 TCP 端口 |
| `version` | string | send.js 版本 |

**退出码:**
- `0`:成功(传输完成或信息显示)
- `1`:错误(查看 stderr 的 JSON 错误详情)

**错误输出 (JSON):**
```json
{
  "error": "ERROR_CODE",
  "message": "Human-readable description"
}
```

错误码:`FILE_NOT_FOUND`、`NOT_A_FILE`、`SERVER_ERROR`、`TIMEOUT`、`READ_ERROR`、`RESPONSE_ERROR`

---

### receive.js

连接发送方并下载文件。

**用法:** `node receive.js <url> <token> <outputPath> [options]`

**参数:**
- `url`(必需):send.js 输出的 URL
- `token`(必需):send.js 输出的安全令牌
- `outputPath`(必需):保存接收文件的路径

**选项:**
- `--timeout <n>`:连接超时秒数(默认:30)
- `--no-progress`:抑制进度更新

**输出 (JSON):**
| 字段 | 类型 | 说明 |
|-------|------|-------------|
| `success` | boolean | 成功时恒为 true |
| `bytesReceived` | number | 实际接收字节 |
| `totalBytes` | number | 预期字节(来自 Content-Length) |
| `duration` | number | 传输时间(秒) |
| `speedMBps` | number | 平均速度 MB/s |
| `outputPath` | string | 保存文件的绝对路径 |

**进度更新(未使用 `--no-progress` 时):**
```json
{
  "progress": true,
  "receivedBytes": 536870912,
  "totalBytes": 1073741824,
  "percent": 50,
  "speedMBps": 125.4
}
```

**退出码:**
- `0`: Success
- `1`:错误(查看 stderr 的 JSON 错误详情)

Error codes: `INVALID_ARGS`, `INVALID_URL`, `CONNECTION_ERROR`, `HTTP_ERROR`, `TIMEOUT`, `WRITE_ERROR`, `SIZE_MISMATCH`, `FILE_EXISTS`, `NO_DATA`

---

### ensure-installed.js

快速检查节点是否已安装 node-transfer。

**用法:** `node ensure-installed.js <targetDir>`

**参数:**
- `targetDir`(必需):要检查的目录

**输出 (JSON):**

已安装:
```json
{
  "installed": true,
  "version": "1.0.0",
  "message": "node-transfer is installed and up-to-date"
}
```

需要安装:
```json
{
  "installed": false,
  "missing": ["send.js"],
  "mismatched": [],
  "currentVersion": null,
  "requiredVersion": "1.0.0",
  "action": "DEPLOY",
  "message": "Installation needed: 1 missing, 0 outdated"
}
```

**退出码:**
- `0`:已安装且为最新
- `1`:需要安装/更新
- `2`:错误(无效目录等)

---

### deploy.js

为主智能体生成部署脚本。

**用法:** `node deploy.js <nodeId> [targetDir]`

**输出:** JSON with:
- `script`:部署文件的 PowerShell 脚本
- `escapedScript`:命令