← 返回
开发者工具 中文

Node Transfer

High-speed, memory-efficient file transfer between OpenClaw nodes using Node.js native streams and token-secured HTTP streaming without Base64 encoding.
使用Node.js原生流和Token安全HTTP流,在OpenClaw节点间进行高速、内存高效的文件传输,无需Base64编码。
eisonme
开发者工具 clawhub v1.0.0 1 版本 100000 Key: 无需
★ 0
Stars
📥 1,364
下载
💾 74
安装
1
版本
#latest

概述

node-transfer

High-speed, memory-efficient file transfer between OpenClaw nodes using native Node.js streams.

📋 Table of Contents


🎯 Problem Solved

The Original Problem

When transferring large files between OpenClaw nodes using the standard nodes.invoke mechanism, we encountered several critical issues:

IssueImpact
---------------
Base64 Encoding Overhead33% larger payload, slower transfers
Memory Exhaustion (OOM)Loading multi-GB files into memory crashes the process
Transfer LatencyJSON serialization/deserialization adds significant delay
9-Minute DeploymentsRe-deploying scripts on every transfer

The Solution

node-transfer uses native HTTP streaming with Node.js streams, providing:

  • Zero memory overhead - Files stream directly from disk to network
  • No Base64 encoding - Raw binary transfer
  • Speed - Line-speed limited only by network bandwidth
  • Install Once, Run Many - Scripts persist on nodes after first deployment

Performance Comparison

MetricBase64 Transfernode-transferImprovement
----------------------------------------------------
1GB file transfer time~15-30 min~8 sec~150x faster
Memory usage1GB+<10MB99% reduction
First transfer overheadN/A~30 sec (one-time install)-
Subsequent transfers~15-30 min<1 sec check + ~8 sec transfer~200x faster

🏗️ Architecture

How It Works

┌──────────────┐     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        │
└──────────────┘                      └──────────────┘

Security Model

  1. One-time Token: 256-bit cryptographically random token (64 hex chars)
  2. Single Connection: Only one download allowed per token
  3. Auto-shutdown: Server closes after transfer completes or disconnects
  4. Token Validation: Every request must include the correct token

Data Flow

  1. Sender (send.js):
    • Generates random port and security token
    • Starts HTTP server on ephemeral port
    • Streams file directly from disk to HTTP response
    • Auto-shutdown after transfer or timeout (5 min default)
  1. Receiver (receive.js):
    • Connects to sender URL with token
    • Streams HTTP response directly to disk
    • Reports progress, speed, and completion status
    • Validates received bytes match expected size

📦 Requirements

  • Node.js: 14.0.0 or higher
  • Network: TCP connectivity between nodes (any port 1024-65535)
  • Firewall: Must allow outbound connections and inbound on ephemeral ports
  • Disk Space: Sufficient space on destination for received files

🚀 Installation

The "Install Once" Pattern

Instead of deploying scripts on every transfer, we deploy them once per node and use a fast version check for subsequent transfers.

Method 1: Using deploy.js (Recommended)

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

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

Method 2: Manual Deployment

On each target node, create the directory and copy files:

# 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

Method 3: Via OpenClaw Agent

// 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 ...
}

💡 Usage

Basic Transfer Workflow

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`);

Using the Command Line

Sender

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

Output:

{
  "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"
}

Options:

node send.js /path/to/file.zip --port 8080 --timeout 10
node send.js --help
node send.js --version

Receiver

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

Output:

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

Options:

node receive.js <url> <token> <output> --timeout 60 --no-progress
node receive.js --help
node receive.js --version

📚 API Reference

send.js

Starts an HTTP server to stream a file.

Usage: node send.js [options]

Arguments:

  • filePath (required): Path to the file to send

Options:

  • --port : Use specific port (default: random ephemeral)
  • --timeout : Timeout in minutes (default: 5)

Output (JSON):

FieldTypeDescription
--------------------------
urlstringHTTP URL for receiver to connect to
tokenstringSecurity token (64 hex chars)
fileSizenumberFile size in bytes
fileNamestringOriginal filename
sourceIpstringIP address of sender
portnumberTCP port used
versionstringVersion of send.js

Exit Codes:

  • 0: Success (transfer completed or info displayed)
  • 1: Error (check stderr for JSON error details)

Error Output (JSON):

{
  "error": "ERROR_CODE",
  "message": "Human-readable description"
}

Error codes: FILE_NOT_FOUND, NOT_A_FILE, SERVER_ERROR, TIMEOUT, READ_ERROR, RESPONSE_ERROR


receive.js

Connects to a sender and downloads a file.

Usage: node receive.js [options]

Arguments:

  • url (required): URL from send.js output
  • token (required): Security token from send.js output
  • outputPath (required): Path to save the received file

Options:

  • --timeout : Connection timeout in seconds (default: 30)
  • --no-progress: Suppress progress updates

Output (JSON):

FieldTypeDescription
--------------------------
successbooleanAlways true on success
bytesReceivednumberActual bytes received
totalBytesnumberExpected bytes (from Content-Length)
durationnumberTransfer time in seconds
speedMBpsnumberAverage speed in MB/s
outputPathstringAbsolute path to saved file

Progress Updates (when not using --no-progress):

{
  "progress": true,
  "receivedBytes": 536870912,
  "totalBytes": 1073741824,
  "percent": 50,
  "speedMBps": 125.4
}

Exit Codes:

  • 0: Success
  • 1: Error (check stderr for JSON error details)

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


ensure-installed.js

Fast check if node-transfer is installed on a node.

Usage: node ensure-installed.js

Arguments:

  • targetDir (required): Directory to check

Output (JSON):

Installed:

{
  "installed": true,
  "version": "1.0.0",
  "message": "node-transfer is installed and up-to-date"
}

Needs installation:

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

Exit Codes:

  • 0: Already installed and up-to-date
  • 1: Needs installation/update
  • 2: Error (invalid directory, etc.)

deploy.js

Generates deployment scripts for the main agent.

Usage: node deploy.js [targetDir]

Output: JSON with:

  • script: PowerShell script to deploy files
  • escapedScript: Escaped version for command-line use
  • usage: Example code for JavaScript and CLI usage

🔧 Troubleshooting

"Connection timeout"

Cause: Network connectivity issue or firewall blocking connection.

Solutions:

  • Verify both nodes can reach each other
  • Check firewall rules allow outbound connections
  • Try specifying a specific port with --port
  • Increase timeout with --timeout

"403 Forbidden: Invalid or missing token"

Cause: Token mismatch or URL manipulation.

Solutions:

  • Use the exact token from send.js output
  • Don't modify the URL
  • Ensure the token hasn't expired (sender times out after 5 minutes)

"409 Conflict: Transfer already in progress"

Cause: Multiple connections attempted with same token.

Solutions:

  • Each sender URL/token can only be used once
  • Start a new sender if you need to retry

"FILE_NOT_FOUND" or "NOT_A_FILE"

Cause: Invalid file path on sender.

Solutions:

  • Use absolute paths
  • Verify file exists
  • Check file permissions

"SIZE_MISMATCH"

Cause: Connection interrupted or network error.

Solutions:

  • Retry the transfer
  • Check network stability
  • The partial file is automatically cleaned up

"Hash mismatch" during ensure-installed

Cause: Files were modified or corrupted.

Solutions:

  • Re-deploy scripts using deploy.js
  • Ensure files are copied without modification
  • Check encoding (must be UTF-8 without BOM)

Slow transfers on subsequent runs

Cause: Not using ensure-installed.js check pattern.

Solutions:

  • Always check installation first (< 100ms)
  • Only deploy if installed: false
  • Follow the "Install Once, Run Many" pattern

📄 Files

FilePurpose
---------------
send.jsHTTP server that streams files to receivers
receive.jsHTTP client that downloads files from senders
ensure-installed.jsFast version/integrity check for deployment
version.jsVersion manifest for update detection
deploy.jsGenerates deployment scripts for agents

🤝 Contributing

See CONTRIBUTING_PROPOSAL.md for information on how this could be integrated into OpenClaw core.


Built for OpenClaw - No Base64, No OOM, No Waiting.

版本历史

共 1 个版本

  • v1.0.0 当前
    2026-03-29 00:33 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

productivity

Ceo Delegation

eisonme
CEO式任务委派工作流程。当收到任何任务时使用此流程:不亲自执行,而是派发子代理执行、监控进度、汇报、验收。适用于需要高效并行处理多任务的场景。
★ 0 📥 1,584
developer-tools

Github

steipete
使用 `gh` CLI 与 GitHub 交互,通过 `gh issue`、`gh pr`、`gh run` 和 `gh api` 管理议题、PR、CI 运行及高级查询。
★ 668 📥 324,145
developer-tools

Gog

steipete
Google Workspace 命令行工具,支持 Gmail、日历、云端硬盘、通讯录、表格和文档。
★ 921 📥 185,794