omniwire 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +316 -0
- package/dist/claude/integration.d.ts +12 -0
- package/dist/claude/integration.js +157 -0
- package/dist/claude/integration.js.map +1 -0
- package/dist/commands/browser.d.ts +2 -0
- package/dist/commands/browser.js +23 -0
- package/dist/commands/browser.js.map +1 -0
- package/dist/commands/builtins.d.ts +4 -0
- package/dist/commands/builtins.js +309 -0
- package/dist/commands/builtins.js.map +1 -0
- package/dist/commands/router.d.ts +2 -0
- package/dist/commands/router.js +174 -0
- package/dist/commands/router.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +198 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +71 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/rest.d.ts +3 -0
- package/dist/mcp/rest.js +98 -0
- package/dist/mcp/rest.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +419 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/sse.d.ts +2 -0
- package/dist/mcp/sse.js +43 -0
- package/dist/mcp/sse.js.map +1 -0
- package/dist/mcp/sync-tools.d.ts +5 -0
- package/dist/mcp/sync-tools.js +124 -0
- package/dist/mcp/sync-tools.js.map +1 -0
- package/dist/nodes/manager.d.ts +28 -0
- package/dist/nodes/manager.js +341 -0
- package/dist/nodes/manager.js.map +1 -0
- package/dist/nodes/realtime.d.ts +13 -0
- package/dist/nodes/realtime.js +46 -0
- package/dist/nodes/realtime.js.map +1 -0
- package/dist/nodes/shell.d.ts +17 -0
- package/dist/nodes/shell.js +137 -0
- package/dist/nodes/shell.js.map +1 -0
- package/dist/nodes/transfer.d.ts +22 -0
- package/dist/nodes/transfer.js +244 -0
- package/dist/nodes/transfer.js.map +1 -0
- package/dist/nodes/tunnel.d.ts +12 -0
- package/dist/nodes/tunnel.js +49 -0
- package/dist/nodes/tunnel.js.map +1 -0
- package/dist/protocol/config.d.ts +9 -0
- package/dist/protocol/config.js +133 -0
- package/dist/protocol/config.js.map +1 -0
- package/dist/protocol/paths.d.ts +3 -0
- package/dist/protocol/paths.js +19 -0
- package/dist/protocol/paths.js.map +1 -0
- package/dist/protocol/types.d.ts +106 -0
- package/dist/protocol/types.js +3 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/sync/db.d.ts +53 -0
- package/dist/sync/db.js +212 -0
- package/dist/sync/db.js.map +1 -0
- package/dist/sync/engine.d.ts +23 -0
- package/dist/sync/engine.js +251 -0
- package/dist/sync/engine.js.map +1 -0
- package/dist/sync/hasher.d.ts +2 -0
- package/dist/sync/hasher.js +25 -0
- package/dist/sync/hasher.js.map +1 -0
- package/dist/sync/index.d.ts +6 -0
- package/dist/sync/index.js +155 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/manifest.d.ts +4 -0
- package/dist/sync/manifest.js +105 -0
- package/dist/sync/manifest.js.map +1 -0
- package/dist/sync/memory-bridge.d.ts +7 -0
- package/dist/sync/memory-bridge.js +84 -0
- package/dist/sync/memory-bridge.js.map +1 -0
- package/dist/sync/paths.d.ts +6 -0
- package/dist/sync/paths.js +53 -0
- package/dist/sync/paths.js.map +1 -0
- package/dist/sync/schema.d.ts +2 -0
- package/dist/sync/schema.js +72 -0
- package/dist/sync/schema.js.map +1 -0
- package/dist/sync/types.d.ts +83 -0
- package/dist/sync/types.js +11 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/sync/watcher.d.ts +21 -0
- package/dist/sync/watcher.js +87 -0
- package/dist/sync/watcher.js.map +1 -0
- package/dist/ui/format.d.ts +24 -0
- package/dist/ui/format.js +108 -0
- package/dist/ui/format.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// OmniWire Shell Manager — persistent root shell sessions with PTY
|
|
2
|
+
// All connections are root — full kernel access by default
|
|
3
|
+
// NOTE: All remote commands in this file run over authenticated SSH2 channels
|
|
4
|
+
// (client.shell() and manager.exec() are SSH2 methods, NOT child_process)
|
|
5
|
+
const IDLE_TIMEOUT_MS = 60_000; // 60s idle timeout
|
|
6
|
+
export class ShellManager {
|
|
7
|
+
manager;
|
|
8
|
+
shells = new Map();
|
|
9
|
+
idCounter = 0;
|
|
10
|
+
constructor(manager) {
|
|
11
|
+
this.manager = manager;
|
|
12
|
+
}
|
|
13
|
+
async openShell(nodeId) {
|
|
14
|
+
const client = this.manager.getClient(nodeId);
|
|
15
|
+
if (!client)
|
|
16
|
+
throw new Error(`Node ${nodeId} is not connected`);
|
|
17
|
+
const channel = await new Promise((resolve, reject) => {
|
|
18
|
+
client.shell({ term: 'xterm-256color', rows: 40, cols: 120 }, (err, stream) => {
|
|
19
|
+
if (err)
|
|
20
|
+
reject(err);
|
|
21
|
+
else
|
|
22
|
+
resolve(stream);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
const id = `shell-${++this.idCounter}`;
|
|
26
|
+
const session = { id, nodeId, startedAt: new Date() };
|
|
27
|
+
const idleTimer = this.createIdleTimer(id);
|
|
28
|
+
this.shells.set(id, { session, channel, idleTimer });
|
|
29
|
+
// Auto-cleanup on channel close
|
|
30
|
+
channel.on('close', () => {
|
|
31
|
+
const s = this.shells.get(id);
|
|
32
|
+
if (s)
|
|
33
|
+
clearTimeout(s.idleTimer);
|
|
34
|
+
this.shells.delete(id);
|
|
35
|
+
});
|
|
36
|
+
return session;
|
|
37
|
+
}
|
|
38
|
+
getChannel(shellId) {
|
|
39
|
+
const shell = this.shells.get(shellId);
|
|
40
|
+
if (shell) {
|
|
41
|
+
// Reset idle timer on access
|
|
42
|
+
clearTimeout(shell.idleTimer);
|
|
43
|
+
shell.idleTimer = this.createIdleTimer(shellId);
|
|
44
|
+
}
|
|
45
|
+
return shell?.channel ?? null;
|
|
46
|
+
}
|
|
47
|
+
closeShell(shellId) {
|
|
48
|
+
const shell = this.shells.get(shellId);
|
|
49
|
+
if (shell) {
|
|
50
|
+
clearTimeout(shell.idleTimer);
|
|
51
|
+
shell.channel.end();
|
|
52
|
+
this.shells.delete(shellId);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
listShells() {
|
|
56
|
+
return Array.from(this.shells.values()).map((s) => s.session);
|
|
57
|
+
}
|
|
58
|
+
closeAll() {
|
|
59
|
+
for (const [id] of this.shells) {
|
|
60
|
+
this.closeShell(id);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
createIdleTimer(shellId) {
|
|
64
|
+
return setTimeout(() => {
|
|
65
|
+
const shell = this.shells.get(shellId);
|
|
66
|
+
if (shell) {
|
|
67
|
+
process.stderr.write(`[shell] ${shellId} timed out after ${IDLE_TIMEOUT_MS / 1000}s idle\n`);
|
|
68
|
+
shell.channel.end();
|
|
69
|
+
this.shells.delete(shellId);
|
|
70
|
+
}
|
|
71
|
+
}, IDLE_TIMEOUT_MS);
|
|
72
|
+
}
|
|
73
|
+
// Interactive shell mode — pipes stdin/stdout directly
|
|
74
|
+
async enterInteractive(nodeId, stdin, stdout, onExit) {
|
|
75
|
+
const session = await this.openShell(nodeId);
|
|
76
|
+
const channel = this.getChannel(session.id);
|
|
77
|
+
// Pipe shell output to terminal
|
|
78
|
+
channel.on('data', (data) => stdout.write(data));
|
|
79
|
+
channel.stderr.on('data', (data) => stdout.write(data));
|
|
80
|
+
// Handle Ctrl+] to exit interactive mode
|
|
81
|
+
const rawMode = stdin.isRaw;
|
|
82
|
+
if ('setRawMode' in stdin) {
|
|
83
|
+
stdin.setRawMode(true);
|
|
84
|
+
}
|
|
85
|
+
const onData = (data) => {
|
|
86
|
+
// Ctrl+] (0x1d) exits interactive mode
|
|
87
|
+
if (data[0] === 0x1d) {
|
|
88
|
+
cleanup();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
channel.write(data);
|
|
92
|
+
};
|
|
93
|
+
const cleanup = () => {
|
|
94
|
+
stdin.removeListener('data', onData);
|
|
95
|
+
if ('setRawMode' in stdin) {
|
|
96
|
+
stdin.setRawMode(rawMode ?? false);
|
|
97
|
+
}
|
|
98
|
+
this.closeShell(session.id);
|
|
99
|
+
onExit();
|
|
100
|
+
};
|
|
101
|
+
stdin.on('data', onData);
|
|
102
|
+
channel.on('close', cleanup);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Kernel-level operations (all nodes are root)
|
|
106
|
+
// Uses manager.exec() — SSH2 client.exec(), NOT child_process
|
|
107
|
+
export async function kernelExec(manager, nodeId, operation, args) {
|
|
108
|
+
let cmd;
|
|
109
|
+
switch (operation) {
|
|
110
|
+
case 'dmesg':
|
|
111
|
+
cmd = `dmesg ${args}`;
|
|
112
|
+
break;
|
|
113
|
+
case 'sysctl':
|
|
114
|
+
cmd = args.includes('=') ? `sysctl -w ${args}` : `sysctl ${args}`;
|
|
115
|
+
break;
|
|
116
|
+
case 'modprobe':
|
|
117
|
+
cmd = `modprobe ${args}`;
|
|
118
|
+
break;
|
|
119
|
+
case 'lsmod':
|
|
120
|
+
cmd = `lsmod ${args}`;
|
|
121
|
+
break;
|
|
122
|
+
case 'strace':
|
|
123
|
+
cmd = `strace ${args}`;
|
|
124
|
+
break;
|
|
125
|
+
case 'perf':
|
|
126
|
+
cmd = `perf ${args}`;
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
cmd = `${operation} ${args}`;
|
|
130
|
+
}
|
|
131
|
+
const result = await manager.exec(nodeId, cmd);
|
|
132
|
+
if (result.code !== 0 && result.stderr) {
|
|
133
|
+
return `Error: ${result.stderr}`;
|
|
134
|
+
}
|
|
135
|
+
return result.stdout;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/nodes/shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NodeManager } from './manager.js';
|
|
2
|
+
import type { TransferResult, TransferMode, FileInfo, DirEntry } from '../protocol/types.js';
|
|
3
|
+
export declare class TransferEngine {
|
|
4
|
+
private manager;
|
|
5
|
+
private statCache;
|
|
6
|
+
constructor(manager: NodeManager);
|
|
7
|
+
transfer(srcNode: string, srcPath: string, dstNode: string, dstPath: string, opts?: {
|
|
8
|
+
mode?: TransferMode;
|
|
9
|
+
}): Promise<TransferResult>;
|
|
10
|
+
private selectMode;
|
|
11
|
+
private findFreePort;
|
|
12
|
+
private transferNetcat;
|
|
13
|
+
private transferAria2c;
|
|
14
|
+
private transferSshPipe;
|
|
15
|
+
private transferSftp;
|
|
16
|
+
readFile(nodeId: string, path: string): Promise<string>;
|
|
17
|
+
writeFile(nodeId: string, path: string, content: string): Promise<void>;
|
|
18
|
+
stat(nodeId: string, path: string): Promise<FileInfo>;
|
|
19
|
+
readdir(nodeId: string, path: string): Promise<DirEntry[]>;
|
|
20
|
+
mkdir(nodeId: string, path: string): Promise<void>;
|
|
21
|
+
unlink(nodeId: string, path: string): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// OmniWire Transfer Engine — raw TCP transfers over WireGuard mesh
|
|
2
|
+
// Modes: netcat+tar (fastest), aria2c (large files), SSH pipe (small files)
|
|
3
|
+
// NOTE: All commands execute via SSH on remote Linux nodes (not local shell).
|
|
4
|
+
// The manager.exec() method uses ssh2 client.exec() for remote nodes, which
|
|
5
|
+
// does not involve local shell interpretation. Path inputs come from trusted
|
|
6
|
+
// terminal/MCP context (operator's own commands).
|
|
7
|
+
import { findNode } from '../protocol/config.js';
|
|
8
|
+
const STAT_CACHE_TTL = 10_000; // 10s stat cache
|
|
9
|
+
export class TransferEngine {
|
|
10
|
+
manager;
|
|
11
|
+
statCache = new Map();
|
|
12
|
+
constructor(manager) {
|
|
13
|
+
this.manager = manager;
|
|
14
|
+
}
|
|
15
|
+
async transfer(srcNode, srcPath, dstNode, dstPath, opts) {
|
|
16
|
+
const start = Date.now();
|
|
17
|
+
// Determine file size for auto mode selection
|
|
18
|
+
const stat = await this.stat(srcNode, srcPath);
|
|
19
|
+
const mode = opts?.mode ?? this.selectMode(stat.size, stat.isDirectory);
|
|
20
|
+
let bytesTransferred = stat.size;
|
|
21
|
+
switch (mode) {
|
|
22
|
+
case 'netcat-tar':
|
|
23
|
+
bytesTransferred = await this.transferNetcat(srcNode, srcPath, dstNode, dstPath, stat.isDirectory);
|
|
24
|
+
break;
|
|
25
|
+
case 'aria2c':
|
|
26
|
+
bytesTransferred = await this.transferAria2c(srcNode, srcPath, dstNode, dstPath);
|
|
27
|
+
break;
|
|
28
|
+
case 'ssh-pipe':
|
|
29
|
+
bytesTransferred = await this.transferSshPipe(srcNode, srcPath, dstNode, dstPath);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const durationMs = Date.now() - start;
|
|
33
|
+
const speedMBps = durationMs > 0 ? (bytesTransferred / 1024 / 1024) / (durationMs / 1000) : 0;
|
|
34
|
+
return { srcNode, dstNode, srcPath, dstPath, mode, bytesTransferred, durationMs, speedMBps };
|
|
35
|
+
}
|
|
36
|
+
selectMode(sizeBytes, isDirectory) {
|
|
37
|
+
if (isDirectory)
|
|
38
|
+
return 'netcat-tar';
|
|
39
|
+
if (sizeBytes < 10 * 1024 * 1024)
|
|
40
|
+
return 'ssh-pipe'; // < 10MB
|
|
41
|
+
if (sizeBytes > 1024 * 1024 * 1024)
|
|
42
|
+
return 'aria2c'; // > 1GB
|
|
43
|
+
return 'netcat-tar'; // 10MB - 1GB
|
|
44
|
+
}
|
|
45
|
+
// Single SSH call to find free port — kernel assigns guaranteed free port via Python
|
|
46
|
+
// Uses manager.exec() which runs via SSH2 client.exec(), not child_process
|
|
47
|
+
async findFreePort(nodeId) {
|
|
48
|
+
const result = await this.manager.exec(nodeId, `python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()"`);
|
|
49
|
+
const port = parseInt(result.stdout.trim());
|
|
50
|
+
if (!port || isNaN(port)) {
|
|
51
|
+
// Fallback: random port
|
|
52
|
+
return 49152 + Math.floor(Math.random() * (65535 - 49152));
|
|
53
|
+
}
|
|
54
|
+
return port;
|
|
55
|
+
}
|
|
56
|
+
async transferNetcat(srcNode, srcPath, dstNode, dstPath, isDirectory) {
|
|
57
|
+
const dstIp = findNode(dstNode)?.host;
|
|
58
|
+
if (!dstIp)
|
|
59
|
+
throw new Error(`Unknown node: ${dstNode}`);
|
|
60
|
+
const port = await this.findFreePort(dstNode);
|
|
61
|
+
const dstDir = isDirectory ? dstPath : dstPath.substring(0, dstPath.lastIndexOf('/')) || '/tmp';
|
|
62
|
+
const srcDir = srcPath.substring(0, srcPath.lastIndexOf('/')) || '/';
|
|
63
|
+
const srcName = srcPath.substring(srcPath.lastIndexOf('/') + 1);
|
|
64
|
+
// Start receiver first (background, timeout 30s) — gzip compressed
|
|
65
|
+
const receiverCmd = `timeout 30 bash -c 'nc -l -p ${port} | tar xzf - -C "${dstDir}"'`;
|
|
66
|
+
const receiverPromise = this.manager.exec(dstNode, `mkdir -p "${dstDir}" && ${receiverCmd}`);
|
|
67
|
+
// Small delay to ensure receiver is listening
|
|
68
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
69
|
+
// Start sender — gzip compressed
|
|
70
|
+
const senderCmd = isDirectory
|
|
71
|
+
? `tar czf - -C "${srcPath}" . | nc -w 10 ${dstIp} ${port}`
|
|
72
|
+
: `tar czf - -C "${srcDir}" "${srcName}" | nc -w 10 ${dstIp} ${port}`;
|
|
73
|
+
const senderResult = await this.manager.exec(srcNode, senderCmd);
|
|
74
|
+
await receiverPromise;
|
|
75
|
+
if (senderResult.code !== 0) {
|
|
76
|
+
throw new Error(`netcat transfer failed: ${senderResult.stderr}`);
|
|
77
|
+
}
|
|
78
|
+
const sizeResult = await this.manager.exec(dstNode, `du -sb "${dstPath}" 2>/dev/null | awk '{print $1}'`);
|
|
79
|
+
return parseInt(sizeResult.stdout.trim()) || 0;
|
|
80
|
+
}
|
|
81
|
+
async transferAria2c(srcNode, srcPath, dstNode, dstPath) {
|
|
82
|
+
const srcIp = findNode(srcNode)?.host;
|
|
83
|
+
if (!srcIp)
|
|
84
|
+
throw new Error(`Unknown node: ${srcNode}`);
|
|
85
|
+
const port = await this.findFreePort(srcNode);
|
|
86
|
+
const srcDir = srcPath.substring(0, srcPath.lastIndexOf('/')) || '/';
|
|
87
|
+
const fileName = srcPath.substring(srcPath.lastIndexOf('/') + 1);
|
|
88
|
+
const dstDir = dstPath.substring(0, dstPath.lastIndexOf('/')) || '/tmp';
|
|
89
|
+
// Start HTTP server on source (background, auto-kill after transfer)
|
|
90
|
+
const serverCmd = `cd "${srcDir}" && timeout 300 python3 -m http.server ${port} --bind 0.0.0.0 &`;
|
|
91
|
+
await this.manager.exec(srcNode, `bash -c '${serverCmd}'`);
|
|
92
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
93
|
+
try {
|
|
94
|
+
const downloadCmd = `mkdir -p "${dstDir}" && aria2c -x16 -s16 --allow-overwrite=true -d "${dstDir}" -o "${fileName}" "http://${srcIp}:${port}/${fileName}" 2>&1`;
|
|
95
|
+
const result = await this.manager.exec(dstNode, downloadCmd);
|
|
96
|
+
if (result.code !== 0)
|
|
97
|
+
throw new Error(`aria2c transfer failed: ${result.stderr || result.stdout}`);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
await this.manager.exec(srcNode, `pkill -f "python3 -m http.server ${port}" 2>/dev/null || true`);
|
|
101
|
+
}
|
|
102
|
+
const sizeResult = await this.manager.exec(dstNode, `stat -c%s "${dstPath}" 2>/dev/null || echo 0`);
|
|
103
|
+
return parseInt(sizeResult.stdout.trim()) || 0;
|
|
104
|
+
}
|
|
105
|
+
async transferSshPipe(srcNode, srcPath, dstNode, dstPath) {
|
|
106
|
+
// Try SFTP first (binary-safe, no encoding overhead)
|
|
107
|
+
const srcClient = this.manager.getClient(srcNode);
|
|
108
|
+
const dstClient = this.manager.getClient(dstNode);
|
|
109
|
+
if (srcClient && dstClient) {
|
|
110
|
+
try {
|
|
111
|
+
return await this.transferSftp(srcClient, dstClient, srcPath, dstPath);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// SFTP failed, fall through to base64 method
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Fallback: base64 over SSH (for local node or SFTP failure)
|
|
118
|
+
const b64Result = await this.manager.exec(srcNode, `base64 "${srcPath}"`);
|
|
119
|
+
if (b64Result.code !== 0)
|
|
120
|
+
throw new Error(`Failed to encode ${srcPath}: ${b64Result.stderr}`);
|
|
121
|
+
const dstDir = dstPath.substring(0, dstPath.lastIndexOf('/')) || '/tmp';
|
|
122
|
+
const b64Content = b64Result.stdout;
|
|
123
|
+
// Use printf + base64 -d to avoid heredoc issues with special content
|
|
124
|
+
const writeCmd = `mkdir -p "${dstDir}" && printf '%s' '${b64Content.replace(/'/g, "'\\''")}' | base64 -d > "${dstPath}"`;
|
|
125
|
+
const writeResult = await this.manager.exec(dstNode, writeCmd);
|
|
126
|
+
if (writeResult.code !== 0)
|
|
127
|
+
throw new Error(`Failed to write ${dstPath} on ${dstNode}: ${writeResult.stderr}`);
|
|
128
|
+
const sizeResult = await this.manager.exec(dstNode, `stat -c%s "${dstPath}" 2>/dev/null || echo 0`);
|
|
129
|
+
return parseInt(sizeResult.stdout.trim()) || 0;
|
|
130
|
+
}
|
|
131
|
+
// SFTP-based transfer — zero encoding overhead, binary-safe
|
|
132
|
+
transferSftp(srcClient, dstClient, srcPath, dstPath) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
srcClient.sftp((sftpErr, srcSftp) => {
|
|
135
|
+
if (sftpErr)
|
|
136
|
+
return reject(sftpErr);
|
|
137
|
+
srcSftp.readFile(srcPath, (readErr, data) => {
|
|
138
|
+
srcSftp.end();
|
|
139
|
+
if (readErr)
|
|
140
|
+
return reject(readErr);
|
|
141
|
+
dstClient.sftp((dstSftpErr, dstSftp) => {
|
|
142
|
+
if (dstSftpErr)
|
|
143
|
+
return reject(dstSftpErr);
|
|
144
|
+
dstSftp.writeFile(dstPath, data, (writeErr) => {
|
|
145
|
+
dstSftp.end();
|
|
146
|
+
if (writeErr)
|
|
147
|
+
return reject(writeErr);
|
|
148
|
+
resolve(data.length);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async readFile(nodeId, path) {
|
|
156
|
+
const result = await this.manager.exec(nodeId, `cat "${path}"`);
|
|
157
|
+
if (result.code !== 0)
|
|
158
|
+
throw new Error(`Failed to read ${path} on ${nodeId}: ${result.stderr}`);
|
|
159
|
+
return result.stdout;
|
|
160
|
+
}
|
|
161
|
+
async writeFile(nodeId, path, content) {
|
|
162
|
+
const dir = path.substring(0, path.lastIndexOf('/')) || '/tmp';
|
|
163
|
+
// Try SFTP first (no heredoc issues, binary-safe)
|
|
164
|
+
const client = this.manager.getClient(nodeId);
|
|
165
|
+
if (client) {
|
|
166
|
+
try {
|
|
167
|
+
// Ensure directory exists
|
|
168
|
+
await this.manager.exec(nodeId, `mkdir -p "${dir}"`);
|
|
169
|
+
await new Promise((resolve, reject) => {
|
|
170
|
+
client.sftp((err, sftp) => {
|
|
171
|
+
if (err)
|
|
172
|
+
return reject(err);
|
|
173
|
+
sftp.writeFile(path, Buffer.from(content, 'utf-8'), (writeErr) => {
|
|
174
|
+
sftp.end();
|
|
175
|
+
writeErr ? reject(writeErr) : resolve();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// SFTP failed, fall through to heredoc
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Fallback: base64 pipe (safe for any content)
|
|
186
|
+
const b64 = Buffer.from(content, 'utf-8').toString('base64');
|
|
187
|
+
const cmd = `mkdir -p "${dir}" && echo '${b64}' | base64 -d > "${path}"`;
|
|
188
|
+
const result = await this.manager.exec(nodeId, cmd);
|
|
189
|
+
if (result.code !== 0)
|
|
190
|
+
throw new Error(`Failed to write ${path} on ${nodeId}: ${result.stderr}`);
|
|
191
|
+
}
|
|
192
|
+
async stat(nodeId, path) {
|
|
193
|
+
// Check cache first
|
|
194
|
+
const cacheKey = `${nodeId}:${path}`;
|
|
195
|
+
const cached = this.statCache.get(cacheKey);
|
|
196
|
+
if (cached && Date.now() - cached.at < STAT_CACHE_TTL) {
|
|
197
|
+
return cached.info;
|
|
198
|
+
}
|
|
199
|
+
const cmd = `stat -c '%s %F %a %Y %U' "${path}" 2>/dev/null && echo OK || echo NOTFOUND`;
|
|
200
|
+
const result = await this.manager.exec(nodeId, cmd);
|
|
201
|
+
if (result.stdout.includes('NOTFOUND') || result.code !== 0) {
|
|
202
|
+
throw new Error(`File not found: ${path} on ${nodeId}`);
|
|
203
|
+
}
|
|
204
|
+
const lines = result.stdout.trim().split('\n');
|
|
205
|
+
const parts = lines[0].split(' ');
|
|
206
|
+
const info = {
|
|
207
|
+
path,
|
|
208
|
+
size: parseInt(parts[0]) || 0,
|
|
209
|
+
isDirectory: parts[1]?.includes('directory') ?? false,
|
|
210
|
+
permissions: parts[2] ?? '000',
|
|
211
|
+
modified: parts[3] ?? '',
|
|
212
|
+
owner: parts[4] ?? 'unknown',
|
|
213
|
+
};
|
|
214
|
+
this.statCache.set(cacheKey, { info, at: Date.now() });
|
|
215
|
+
return info;
|
|
216
|
+
}
|
|
217
|
+
async readdir(nodeId, path) {
|
|
218
|
+
const cmd = `ls -la --time-style=long-iso "${path}" 2>/dev/null | tail -n +2`;
|
|
219
|
+
const result = await this.manager.exec(nodeId, cmd);
|
|
220
|
+
if (result.code !== 0)
|
|
221
|
+
throw new Error(`Failed to list ${path} on ${nodeId}: ${result.stderr}`);
|
|
222
|
+
return result.stdout.split('\n').filter(Boolean).map((line) => {
|
|
223
|
+
const parts = line.split(/\s+/);
|
|
224
|
+
return {
|
|
225
|
+
name: parts.slice(7).join(' '),
|
|
226
|
+
size: parseInt(parts[4]) || 0,
|
|
227
|
+
isDirectory: line.startsWith('d'),
|
|
228
|
+
permissions: parts[0] ?? '',
|
|
229
|
+
modified: `${parts[5]} ${parts[6]}`,
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async mkdir(nodeId, path) {
|
|
234
|
+
const result = await this.manager.exec(nodeId, `mkdir -p "${path}"`);
|
|
235
|
+
if (result.code !== 0)
|
|
236
|
+
throw new Error(`Failed to mkdir ${path} on ${nodeId}: ${result.stderr}`);
|
|
237
|
+
}
|
|
238
|
+
async unlink(nodeId, path) {
|
|
239
|
+
const result = await this.manager.exec(nodeId, `rm -f "${path}"`);
|
|
240
|
+
if (result.code !== 0)
|
|
241
|
+
throw new Error(`Failed to unlink ${path} on ${nodeId}: ${result.stderr}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/nodes/transfer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NodeManager } from './manager.js';
|
|
2
|
+
import type { TunnelInfo } from '../protocol/types.js';
|
|
3
|
+
export declare class TunnelManager {
|
|
4
|
+
private manager;
|
|
5
|
+
private tunnels;
|
|
6
|
+
private idCounter;
|
|
7
|
+
constructor(manager: NodeManager);
|
|
8
|
+
create(nodeId: string, localPort: number, remotePort: number, remoteHost?: string): Promise<TunnelInfo>;
|
|
9
|
+
close(tunnelId: string): void;
|
|
10
|
+
list(): TunnelInfo[];
|
|
11
|
+
closeAll(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// OmniWire Tunnel Manager — SSH port forwarding over WireGuard mesh
|
|
2
|
+
import { createServer } from 'node:net';
|
|
3
|
+
export class TunnelManager {
|
|
4
|
+
manager;
|
|
5
|
+
tunnels = new Map();
|
|
6
|
+
idCounter = 0;
|
|
7
|
+
constructor(manager) {
|
|
8
|
+
this.manager = manager;
|
|
9
|
+
}
|
|
10
|
+
async create(nodeId, localPort, remotePort, remoteHost = '127.0.0.1') {
|
|
11
|
+
const client = this.manager.getClient(nodeId);
|
|
12
|
+
if (!client)
|
|
13
|
+
throw new Error(`Node ${nodeId} is not connected`);
|
|
14
|
+
const id = `tunnel-${++this.idCounter}`;
|
|
15
|
+
const server = createServer((socket) => {
|
|
16
|
+
client.forwardOut('127.0.0.1', localPort, remoteHost, remotePort, (err, stream) => {
|
|
17
|
+
if (err) {
|
|
18
|
+
socket.end();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
socket.pipe(stream);
|
|
22
|
+
stream.pipe(socket);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
await new Promise((resolve, reject) => {
|
|
26
|
+
server.listen(localPort, '127.0.0.1', () => resolve());
|
|
27
|
+
server.on('error', reject);
|
|
28
|
+
});
|
|
29
|
+
const info = { id, nodeId, localPort, remotePort, remoteHost };
|
|
30
|
+
this.tunnels.set(id, { ...info, server });
|
|
31
|
+
return info;
|
|
32
|
+
}
|
|
33
|
+
close(tunnelId) {
|
|
34
|
+
const tunnel = this.tunnels.get(tunnelId);
|
|
35
|
+
if (tunnel) {
|
|
36
|
+
tunnel.server.close();
|
|
37
|
+
this.tunnels.delete(tunnelId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
list() {
|
|
41
|
+
return Array.from(this.tunnels.values()).map(({ server: _, ...info }) => info);
|
|
42
|
+
}
|
|
43
|
+
closeAll() {
|
|
44
|
+
for (const [id] of this.tunnels) {
|
|
45
|
+
this.close(id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=tunnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../src/nodes/tunnel.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAIpE,OAAO,EAAE,YAAY,EAAe,MAAM,UAAU,CAAC;AAMrD,MAAM,OAAO,aAAa;IAIJ;IAHZ,OAAO,GAA8B,IAAI,GAAG,EAAE,CAAC;IAC/C,SAAS,GAAG,CAAC,CAAC;IAEtB,YAAoB,OAAoB;QAApB,YAAO,GAAP,OAAO,CAAa;IAAG,CAAC;IAE5C,KAAK,CAAC,MAAM,CACV,MAAc,EACd,SAAiB,EACjB,UAAkB,EAClB,aAAqB,WAAW;QAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,MAAM,mBAAmB,CAAC,CAAC;QAEhE,MAAM,EAAE,GAAG,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QAExC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;YACrC,MAAM,CAAC,UAAU,CACf,WAAW,EACX,SAAS,EACT,UAAU,EACV,UAAU,EACV,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;gBACd,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,GAAG,EAAE,CAAC;oBACb,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACpB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACvD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAe,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;QAC3E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAE1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,QAAgB;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IACjF,CAAC;IAED,QAAQ;QACN,KAAK,MAAM,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MeshConfig, MeshNode, NodeRole } from './types.js';
|
|
2
|
+
export declare function localNodeId(): string;
|
|
3
|
+
export declare const NODE_ROLES: Record<string, NodeRole>;
|
|
4
|
+
export declare function getNodeForRole(role: NodeRole): MeshNode | undefined;
|
|
5
|
+
export declare function getDefaultNodeForTask(task: 'storage' | 'browser' | 'compute' | 'local'): string;
|
|
6
|
+
export declare const CONFIG: MeshConfig;
|
|
7
|
+
export declare function findNode(query: string): MeshNode | undefined;
|
|
8
|
+
export declare function remoteNodes(): MeshNode[];
|
|
9
|
+
export declare function allNodes(): MeshNode[];
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// OmniWire mesh configuration — loaded from ~/.omniwire/mesh.json or env
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
5
|
+
const home = homedir();
|
|
6
|
+
const sshDir = join(home, '.ssh');
|
|
7
|
+
function resolveIdentityFile(identityFile) {
|
|
8
|
+
if (!identityFile)
|
|
9
|
+
return '';
|
|
10
|
+
// If already absolute, return as-is
|
|
11
|
+
if (identityFile.startsWith('/') || identityFile.includes(':'))
|
|
12
|
+
return identityFile;
|
|
13
|
+
return join(sshDir, identityFile);
|
|
14
|
+
}
|
|
15
|
+
function buildLocalNode() {
|
|
16
|
+
const isWindows = process.platform === 'win32';
|
|
17
|
+
return {
|
|
18
|
+
id: localNodeId(),
|
|
19
|
+
alias: isWindows ? 'local' : 'local',
|
|
20
|
+
host: '127.0.0.1',
|
|
21
|
+
port: 0,
|
|
22
|
+
user: isWindows ? (process.env.USERNAME ?? 'user') : (process.env.USER ?? 'user'),
|
|
23
|
+
identityFile: '',
|
|
24
|
+
os: isWindows ? 'windows' : 'linux',
|
|
25
|
+
isLocal: true,
|
|
26
|
+
tags: ['local', isWindows ? 'windows' : 'linux'],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function localNodeId() {
|
|
30
|
+
if (process.platform === 'win32')
|
|
31
|
+
return 'windows';
|
|
32
|
+
return process.env.OMNIWIRE_NODE_ID ?? 'local';
|
|
33
|
+
}
|
|
34
|
+
function loadMeshJson() {
|
|
35
|
+
// 1. Try ~/.omniwire/mesh.json
|
|
36
|
+
const configPath = join(home, '.omniwire', 'mesh.json');
|
|
37
|
+
if (existsSync(configPath)) {
|
|
38
|
+
try {
|
|
39
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
40
|
+
return JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
process.stderr.write(`[omniwire] Warning: failed to parse ${configPath}\n`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 2. Try OMNIWIRE_CONFIG env var (JSON string)
|
|
47
|
+
const envConfig = process.env.OMNIWIRE_CONFIG;
|
|
48
|
+
if (envConfig) {
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(envConfig);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
process.stderr.write('[omniwire] Warning: failed to parse OMNIWIRE_CONFIG env var\n');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function buildConfig() {
|
|
59
|
+
const json = loadMeshJson();
|
|
60
|
+
const localNode = buildLocalNode();
|
|
61
|
+
if (!json || !json.nodes || json.nodes.length === 0) {
|
|
62
|
+
// Minimal fallback: local node only
|
|
63
|
+
return {
|
|
64
|
+
nodes: [localNode],
|
|
65
|
+
roles: { [localNode.id]: 'controller' },
|
|
66
|
+
meshSubnet: '10.0.0.0/24',
|
|
67
|
+
claudePath: 'claude',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const remoteNodes = json.nodes.map((n) => ({
|
|
71
|
+
id: n.id,
|
|
72
|
+
alias: n.alias ?? n.id.slice(0, 3),
|
|
73
|
+
host: n.host,
|
|
74
|
+
port: n.port ?? 22,
|
|
75
|
+
user: n.user,
|
|
76
|
+
identityFile: resolveIdentityFile(n.identityFile),
|
|
77
|
+
os: n.os ?? 'linux',
|
|
78
|
+
isLocal: n.isLocal ?? false,
|
|
79
|
+
tags: n.tags ?? [],
|
|
80
|
+
}));
|
|
81
|
+
// Prepend local node if not already in json
|
|
82
|
+
const hasLocal = remoteNodes.some((n) => n.isLocal);
|
|
83
|
+
const nodes = hasLocal ? remoteNodes : [localNode, ...remoteNodes];
|
|
84
|
+
// Build roles map: from json.roles, then fall back to per-node role fields
|
|
85
|
+
const roles = { [localNode.id]: 'controller' };
|
|
86
|
+
if (json.roles) {
|
|
87
|
+
Object.assign(roles, json.roles);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
for (const n of json.nodes) {
|
|
91
|
+
if (n.role)
|
|
92
|
+
roles[n.id] = n.role;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
nodes,
|
|
97
|
+
roles,
|
|
98
|
+
meshSubnet: json.meshSubnet ?? '10.0.0.0/24',
|
|
99
|
+
claudePath: json.claudePath ?? 'claude',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const _config = buildConfig();
|
|
103
|
+
const NODES = _config.nodes;
|
|
104
|
+
export const NODE_ROLES = _config.roles;
|
|
105
|
+
export function getNodeForRole(role) {
|
|
106
|
+
const id = Object.entries(NODE_ROLES).find(([, r]) => r === role)?.[0];
|
|
107
|
+
return id ? NODES.find((n) => n.id === id) : undefined;
|
|
108
|
+
}
|
|
109
|
+
export function getDefaultNodeForTask(task) {
|
|
110
|
+
switch (task) {
|
|
111
|
+
case 'storage': return getNodeForRole('storage')?.id ?? localNodeId();
|
|
112
|
+
case 'browser': return getNodeForRole('gpu+browser')?.id ?? localNodeId();
|
|
113
|
+
case 'compute': return getNodeForRole('compute')?.id ?? getNodeForRole('storage')?.id ?? localNodeId();
|
|
114
|
+
case 'local': return localNodeId();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export const CONFIG = {
|
|
118
|
+
nodes: NODES,
|
|
119
|
+
defaultNode: 'local',
|
|
120
|
+
meshSubnet: _config.meshSubnet,
|
|
121
|
+
claudePath: _config.claudePath,
|
|
122
|
+
};
|
|
123
|
+
export function findNode(query) {
|
|
124
|
+
const q = query.toLowerCase();
|
|
125
|
+
return CONFIG.nodes.find((n) => n.id === q || n.alias === q || n.host === q);
|
|
126
|
+
}
|
|
127
|
+
export function remoteNodes() {
|
|
128
|
+
return CONFIG.nodes.filter((n) => !n.isLocal);
|
|
129
|
+
}
|
|
130
|
+
export function allNodes() {
|
|
131
|
+
return [...CONFIG.nodes];
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/protocol/config.ts"],"names":[],"mappings":"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"}
|