ezshare-cli 1.0.1 → 1.0.3
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/dist/commands/receive.js +23 -10
- package/dist/commands/send.js +22 -4
- package/dist/utils/compression.js +11 -2
- package/package.json +1 -1
package/dist/commands/receive.js
CHANGED
|
@@ -4,10 +4,23 @@ import { Box, Text } from 'ink';
|
|
|
4
4
|
import { Spinner, ProgressBar } from '@inkjs/ui';
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import { Transform } from 'node:stream';
|
|
7
|
+
import { appendFileSync } from 'node:fs';
|
|
7
8
|
import { parseTopicKey, deriveKey, createDecryptStream } from '../utils/crypto.js';
|
|
8
9
|
import { createDecompressStream } from '../utils/compression.js';
|
|
9
10
|
import { createExtractStream } from '../utils/tar.js';
|
|
10
11
|
import { createReceiverSwarm, cleanupSwarm } from '../utils/network.js';
|
|
12
|
+
// Debug logging to file (Ink captures stdout)
|
|
13
|
+
const LOG_FILE = '/tmp/ezshare_debug.log';
|
|
14
|
+
function debugLog(message) {
|
|
15
|
+
const timestamp = new Date().toISOString();
|
|
16
|
+
const logLine = `[${timestamp}] ${message}\n`;
|
|
17
|
+
try {
|
|
18
|
+
appendFileSync(LOG_FILE, logLine);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
// Ignore file errors
|
|
22
|
+
}
|
|
23
|
+
}
|
|
11
24
|
export function ReceiveCommand({ shareKey, outputPath = process.cwd(), onComplete, onError }) {
|
|
12
25
|
const [state, setState] = useState('connecting');
|
|
13
26
|
const [progress, setProgress] = useState(0);
|
|
@@ -26,16 +39,16 @@ export function ReceiveCommand({ shareKey, outputPath = process.cwd(), onComplet
|
|
|
26
39
|
const { swarm, connectionPromise } = await createReceiverSwarm(topic);
|
|
27
40
|
// Wait for peer connection (listener already registered)
|
|
28
41
|
const socket = await connectionPromise;
|
|
29
|
-
|
|
42
|
+
debugLog('[Receiver] Connected to sender');
|
|
30
43
|
// Add socket error handler
|
|
31
44
|
socket.on('error', (err) => {
|
|
32
|
-
|
|
45
|
+
debugLog('[Receiver] Socket error: ' + err);
|
|
33
46
|
});
|
|
34
47
|
socket.on('end', () => {
|
|
35
|
-
|
|
48
|
+
debugLog('[Receiver] Socket end event received');
|
|
36
49
|
});
|
|
37
50
|
socket.on('close', () => {
|
|
38
|
-
|
|
51
|
+
debugLog('[Receiver] Socket closed');
|
|
39
52
|
});
|
|
40
53
|
// Read metadata first (JSON header followed by newline)
|
|
41
54
|
const metadataBuffer = [];
|
|
@@ -52,19 +65,19 @@ export function ReceiveCommand({ shareKey, outputPath = process.cwd(), onComplet
|
|
|
52
65
|
// Found metadata
|
|
53
66
|
try {
|
|
54
67
|
const metadataJson = combined.slice(0, newlineIndex).toString();
|
|
55
|
-
|
|
68
|
+
debugLog('[Receiver] Received metadata: ' + metadataJson);
|
|
56
69
|
transferMetadata = JSON.parse(metadataJson);
|
|
57
70
|
setMetadata(transferMetadata);
|
|
58
71
|
// Push remaining data after newline
|
|
59
72
|
const remainingData = combined.slice(newlineIndex + 1);
|
|
60
|
-
|
|
73
|
+
debugLog(`[Receiver] Metadata extracted, ${remainingData.length} bytes of data after newline`);
|
|
61
74
|
if (remainingData.length > 0) {
|
|
62
75
|
this.push(remainingData);
|
|
63
76
|
}
|
|
64
77
|
metadataReceived = true;
|
|
65
78
|
}
|
|
66
79
|
catch (err) {
|
|
67
|
-
|
|
80
|
+
debugLog('[Receiver] Failed to parse metadata: ' + err);
|
|
68
81
|
callback(err);
|
|
69
82
|
return;
|
|
70
83
|
}
|
|
@@ -91,18 +104,18 @@ export function ReceiveCommand({ shareKey, outputPath = process.cwd(), onComplet
|
|
|
91
104
|
});
|
|
92
105
|
// Start receiving
|
|
93
106
|
setState('receiving');
|
|
94
|
-
|
|
107
|
+
debugLog('[Receiver] Starting receive pipeline');
|
|
95
108
|
// Build the pipeline: Socket → Metadata Extractor → Progress → Decrypt → Decompress → Tar Extract
|
|
96
109
|
const decryptStream = createDecryptStream(encryptionKey);
|
|
97
110
|
const decompressStream = await createDecompressStream();
|
|
98
111
|
const extractStream = createExtractStream(outputPath);
|
|
99
112
|
await pipeline(socket, metadataExtractor, progressTracker, decryptStream, decompressStream, extractStream);
|
|
100
|
-
|
|
113
|
+
debugLog('[Receiver] Pipeline completed successfully');
|
|
101
114
|
// Transfer complete
|
|
102
115
|
setState('done');
|
|
103
116
|
// Receiver cleanup - small delay to ensure socket is fully closed
|
|
104
117
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
105
|
-
|
|
118
|
+
debugLog('[Receiver] Cleaning up receiver swarm');
|
|
106
119
|
await cleanupSwarm(swarm);
|
|
107
120
|
if (onComplete) {
|
|
108
121
|
onComplete();
|
package/dist/commands/send.js
CHANGED
|
@@ -4,10 +4,23 @@ import { Box, Text } from 'ink';
|
|
|
4
4
|
import { Spinner, ProgressBar } from '@inkjs/ui';
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import { Transform } from 'node:stream';
|
|
7
|
+
import { appendFileSync } from 'node:fs';
|
|
7
8
|
import { generateTopicKey, deriveKey, createEncryptStream } from '../utils/crypto.js';
|
|
8
9
|
import { createCompressStream, shouldCompress } from '../utils/compression.js';
|
|
9
10
|
import { createPackStream, getTransferMetadata } from '../utils/tar.js';
|
|
10
11
|
import { createSenderSwarm, cleanupSwarm } from '../utils/network.js';
|
|
12
|
+
// Debug logging to file (Ink captures stdout)
|
|
13
|
+
const LOG_FILE = '/tmp/ezshare_debug.log';
|
|
14
|
+
function debugLog(message) {
|
|
15
|
+
const timestamp = new Date().toISOString();
|
|
16
|
+
const logLine = `[${timestamp}] ${message}\n`;
|
|
17
|
+
try {
|
|
18
|
+
appendFileSync(LOG_FILE, logLine);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
// Ignore file errors
|
|
22
|
+
}
|
|
23
|
+
}
|
|
11
24
|
export function SendCommand({ path, onComplete, onError }) {
|
|
12
25
|
const [state, setState] = useState('init');
|
|
13
26
|
const [shareKey, setShareKey] = useState('');
|
|
@@ -32,14 +45,18 @@ export function SendCommand({ path, onComplete, onError }) {
|
|
|
32
45
|
});
|
|
33
46
|
// Create sender swarm
|
|
34
47
|
setState('waiting');
|
|
48
|
+
debugLog('[Sender] About to call createSenderSwarm');
|
|
35
49
|
const { swarm, waitForPeer } = await createSenderSwarm(topic);
|
|
50
|
+
debugLog('[Sender] createSenderSwarm completed, got waitForPeer function');
|
|
36
51
|
// Wait for peer connection
|
|
52
|
+
debugLog('[Sender] Calling waitForPeer()...');
|
|
37
53
|
const socket = await waitForPeer();
|
|
54
|
+
debugLog('[Sender] Got socket from waitForPeer');
|
|
38
55
|
// Register socket close listener BEFORE pipeline starts
|
|
39
56
|
// This ensures we don't miss the close event
|
|
40
57
|
const socketClosed = new Promise((resolve) => {
|
|
41
58
|
socket.once('close', () => {
|
|
42
|
-
|
|
59
|
+
debugLog('[Sender] Socket closed');
|
|
43
60
|
resolve();
|
|
44
61
|
});
|
|
45
62
|
});
|
|
@@ -47,9 +64,10 @@ export function SendCommand({ path, onComplete, onError }) {
|
|
|
47
64
|
socket.on('error', (err) => {
|
|
48
65
|
console.error('[Sender] Socket error:', err);
|
|
49
66
|
});
|
|
67
|
+
debugLog('[Sender] About to set state to sending');
|
|
50
68
|
// Peer connected! Start sending
|
|
51
69
|
setState('sending');
|
|
52
|
-
|
|
70
|
+
debugLog('[Sender] Starting transfer pipeline');
|
|
53
71
|
// Create metadata prepend stream
|
|
54
72
|
const metadataJson = JSON.stringify({
|
|
55
73
|
totalSize: transferMetadata.totalSize,
|
|
@@ -84,11 +102,11 @@ export function SendCommand({ path, onComplete, onError }) {
|
|
|
84
102
|
const compressStream = await createCompressStream(shouldCompress(path));
|
|
85
103
|
const encryptStream = createEncryptStream(encryptionKey);
|
|
86
104
|
await pipeline(packStream, compressStream, encryptStream, metadataPrepender, progressTracker, socket);
|
|
87
|
-
|
|
105
|
+
debugLog('[Sender] Pipeline completed, waiting for socket to close');
|
|
88
106
|
// Wait for socket to fully close before cleaning up
|
|
89
107
|
// This ensures the receiver has finished and closed their end
|
|
90
108
|
await socketClosed;
|
|
91
|
-
|
|
109
|
+
debugLog('[Sender] Socket closed, cleaning up swarm');
|
|
92
110
|
// Transfer complete
|
|
93
111
|
setState('done');
|
|
94
112
|
await cleanupSwarm(swarm);
|
|
@@ -16,7 +16,14 @@
|
|
|
16
16
|
import { Transform } from 'node:stream';
|
|
17
17
|
import { extname } from 'node:path';
|
|
18
18
|
import { execSync } from 'node:child_process';
|
|
19
|
-
import
|
|
19
|
+
// Dynamic import to handle CommonJS module from ES module context
|
|
20
|
+
let zstdModule = null;
|
|
21
|
+
async function getZstdModule() {
|
|
22
|
+
if (!zstdModule) {
|
|
23
|
+
zstdModule = await import('simple-zstd');
|
|
24
|
+
}
|
|
25
|
+
return zstdModule;
|
|
26
|
+
}
|
|
20
27
|
// Protocol flags
|
|
21
28
|
const FLAG_RAW = 0x00;
|
|
22
29
|
const FLAG_COMPRESSED = 0x01;
|
|
@@ -150,6 +157,7 @@ export async function createCompressStream(shouldCompress = true) {
|
|
|
150
157
|
' Windows: choco install zstd');
|
|
151
158
|
}
|
|
152
159
|
// Get zstd compression stream
|
|
160
|
+
const { compress } = await getZstdModule();
|
|
153
161
|
const zstdStream = await compress(COMPRESSION_LEVEL);
|
|
154
162
|
// Track state
|
|
155
163
|
let flagSent = false;
|
|
@@ -257,7 +265,8 @@ export async function createDecompressStream() {
|
|
|
257
265
|
return callback(new Error('zstd is not installed but data is compressed'));
|
|
258
266
|
}
|
|
259
267
|
// Synchronously set up - decompress() returns Promise but we handle async carefully
|
|
260
|
-
|
|
268
|
+
getZstdModule()
|
|
269
|
+
.then(({ decompress }) => decompress())
|
|
261
270
|
.then((stream) => {
|
|
262
271
|
if (destroyed) {
|
|
263
272
|
stream.destroy();
|