@syncast/cli 0.1.1 → 0.1.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/cli.js +160 -7
- package/dist/index.js +162 -8
- package/dist/ws-server.d.ts +5 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,138 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { CHUNK_SIZE, DEFAULT_HOST, DEFAULT_PORT, ErrorCodes, FILE_CHANGE_DEBOUNCE_MS, FILE_STABLE_CHECK_INTERVAL_MS, createConnectedMessage, createErrorMessage, createFileConfig, createFolderConfig, getFileConfigPath, getFilename, getFolderConfigPath, isConfigFile, isSupportedFile, parseFileConfig, parseFolderConfig, toRelativePath } from "local-sync-protocol";
|
|
4
3
|
import { WebSocketServer } from "ws";
|
|
5
4
|
import { mkdir, readFile, readdir, stat as promises_stat, writeFile } from "node:fs/promises";
|
|
6
5
|
import { dirname as external_node_path_dirname, join } from "node:path";
|
|
7
6
|
import { exec } from "node:child_process";
|
|
8
7
|
import { promisify } from "node:util";
|
|
9
8
|
import { watch } from "chokidar";
|
|
9
|
+
const DEFAULT_HOST = "localhost";
|
|
10
|
+
const CHUNK_SIZE = 1048576;
|
|
11
|
+
const FILE_CONFIG_SUFFIX = ".config.json";
|
|
12
|
+
const FOLDER_CONFIG_SUFFIX = ".folder.config.json";
|
|
13
|
+
const SUPPORTED_EXTENSIONS = [
|
|
14
|
+
"jpg",
|
|
15
|
+
"jpeg",
|
|
16
|
+
"png",
|
|
17
|
+
"webp",
|
|
18
|
+
"gif",
|
|
19
|
+
"mp4",
|
|
20
|
+
"mov",
|
|
21
|
+
"m4v",
|
|
22
|
+
"webm",
|
|
23
|
+
"mp3",
|
|
24
|
+
"wav",
|
|
25
|
+
"ogg",
|
|
26
|
+
"m4a"
|
|
27
|
+
];
|
|
28
|
+
function createFileConfig(assetId, originalFilename, md5) {
|
|
29
|
+
return {
|
|
30
|
+
assetId,
|
|
31
|
+
syncedAt: new Date().toISOString(),
|
|
32
|
+
originalFilename,
|
|
33
|
+
md5
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createFolderConfig(folderId) {
|
|
37
|
+
return {
|
|
38
|
+
folderId,
|
|
39
|
+
syncedAt: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function parseFileConfig(content) {
|
|
43
|
+
try {
|
|
44
|
+
const config = JSON.parse(content);
|
|
45
|
+
if (!config.assetId || "string" != typeof config.assetId) {
|
|
46
|
+
console.warn("Invalid config.json: missing or invalid assetId");
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (!config.originalFilename || "string" != typeof config.originalFilename) {
|
|
50
|
+
console.warn("Invalid config.json: missing or invalid originalFilename");
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return config;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.warn("Failed to parse config.json:", e);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function parseFolderConfig(content) {
|
|
60
|
+
try {
|
|
61
|
+
const config = JSON.parse(content);
|
|
62
|
+
if (!config.folderId || "string" != typeof config.folderId) {
|
|
63
|
+
console.warn("Invalid folder config.json: missing or invalid folderId");
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return config;
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.warn("Failed to parse folder config.json:", e);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function getFileConfigPath(filePath) {
|
|
73
|
+
return `${filePath}${FILE_CONFIG_SUFFIX}`;
|
|
74
|
+
}
|
|
75
|
+
function getFolderConfigPath(folderPath) {
|
|
76
|
+
const normalizedPath = folderPath.replace(/\/+$/, "");
|
|
77
|
+
return `${normalizedPath}${FOLDER_CONFIG_SUFFIX}`;
|
|
78
|
+
}
|
|
79
|
+
function isConfigFile(path) {
|
|
80
|
+
return path.endsWith(FOLDER_CONFIG_SUFFIX) || path.endsWith(FILE_CONFIG_SUFFIX);
|
|
81
|
+
}
|
|
82
|
+
function getFileExtension(filename) {
|
|
83
|
+
const lastDot = filename.lastIndexOf(".");
|
|
84
|
+
if (-1 === lastDot || lastDot === filename.length - 1) return "";
|
|
85
|
+
return filename.slice(lastDot + 1).toLowerCase();
|
|
86
|
+
}
|
|
87
|
+
function isSupportedFile(filename) {
|
|
88
|
+
const ext = getFileExtension(filename);
|
|
89
|
+
return ext ? SUPPORTED_EXTENSIONS.includes(ext) : false;
|
|
90
|
+
}
|
|
91
|
+
function getFilename(path) {
|
|
92
|
+
const normalized = path.replace(/\\/g, "/");
|
|
93
|
+
const parts = normalized.split("/");
|
|
94
|
+
return parts[parts.length - 1] || "";
|
|
95
|
+
}
|
|
96
|
+
function normalizePath(path) {
|
|
97
|
+
return path.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
98
|
+
}
|
|
99
|
+
function toRelativePath(absolutePath, basePath) {
|
|
100
|
+
const normalizedAbs = normalizePath(absolutePath);
|
|
101
|
+
const normalizedBase = normalizePath(basePath);
|
|
102
|
+
if (!normalizedAbs.startsWith(normalizedBase)) return normalizedAbs;
|
|
103
|
+
let relative = normalizedAbs.slice(normalizedBase.length);
|
|
104
|
+
if (relative.startsWith("/")) relative = relative.slice(1);
|
|
105
|
+
return relative;
|
|
106
|
+
}
|
|
107
|
+
function createConnectedMessage(cwd) {
|
|
108
|
+
return {
|
|
109
|
+
type: "connected",
|
|
110
|
+
cwd,
|
|
111
|
+
version: "1.0.0"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createErrorMessage(code, message, requestId) {
|
|
115
|
+
return {
|
|
116
|
+
type: "error",
|
|
117
|
+
code,
|
|
118
|
+
message,
|
|
119
|
+
requestId
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const ErrorCodes = {
|
|
123
|
+
FILE_NOT_FOUND: "FILE_NOT_FOUND",
|
|
124
|
+
FOLDER_NOT_FOUND: "FOLDER_NOT_FOUND",
|
|
125
|
+
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
126
|
+
FILE_IN_USE: "FILE_IN_USE",
|
|
127
|
+
TRANSFER_CANCELLED: "TRANSFER_CANCELLED",
|
|
128
|
+
INVALID_REQUEST_ID: "INVALID_REQUEST_ID",
|
|
129
|
+
VERSION_MISMATCH: "VERSION_MISMATCH",
|
|
130
|
+
PROJECT_ALREADY_CONNECTED: "PROJECT_ALREADY_CONNECTED",
|
|
131
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR"
|
|
132
|
+
};
|
|
133
|
+
var __webpack_exports__DEFAULT_PORT = 23456;
|
|
134
|
+
var __webpack_exports__FILE_CHANGE_DEBOUNCE_MS = 300;
|
|
135
|
+
var __webpack_exports__FILE_STABLE_CHECK_INTERVAL_MS = 500;
|
|
10
136
|
class FileTransferManager {
|
|
11
137
|
rootPath;
|
|
12
138
|
events;
|
|
@@ -136,7 +262,7 @@ class FileChangeCollector {
|
|
|
136
262
|
this.timer = setTimeout(()=>{
|
|
137
263
|
this.flush();
|
|
138
264
|
this.timer = null;
|
|
139
|
-
},
|
|
265
|
+
}, __webpack_exports__FILE_CHANGE_DEBOUNCE_MS);
|
|
140
266
|
}
|
|
141
267
|
flush() {
|
|
142
268
|
if (0 === this.pending.size) return;
|
|
@@ -252,7 +378,7 @@ class FileWatcher {
|
|
|
252
378
|
persistent: true,
|
|
253
379
|
ignoreInitial: true,
|
|
254
380
|
awaitWriteFinish: {
|
|
255
|
-
stabilityThreshold:
|
|
381
|
+
stabilityThreshold: __webpack_exports__FILE_STABLE_CHECK_INTERVAL_MS,
|
|
256
382
|
pollInterval: 100
|
|
257
383
|
}
|
|
258
384
|
});
|
|
@@ -359,7 +485,7 @@ class SyncServer {
|
|
|
359
485
|
isProcessingQueue = false;
|
|
360
486
|
currentProjectId = null;
|
|
361
487
|
constructor(options = {}, events = {}){
|
|
362
|
-
this.port = options.port ??
|
|
488
|
+
this.port = options.port ?? __webpack_exports__DEFAULT_PORT;
|
|
363
489
|
this.host = options.host ?? DEFAULT_HOST;
|
|
364
490
|
this.cwd = process.cwd();
|
|
365
491
|
this.events = events;
|
|
@@ -499,6 +625,9 @@ class SyncServer {
|
|
|
499
625
|
case "write_config":
|
|
500
626
|
await this.handleWriteConfig(message.path, message.assetId, message.folderId, message.originalFilename, message.md5);
|
|
501
627
|
break;
|
|
628
|
+
case "create_folder":
|
|
629
|
+
await this.handleCreateFolder(message.path, message.folderId);
|
|
630
|
+
break;
|
|
502
631
|
case "cancel_transfer":
|
|
503
632
|
await this.handleCancelTransfer(message.requestId);
|
|
504
633
|
break;
|
|
@@ -644,6 +773,30 @@ class SyncServer {
|
|
|
644
773
|
this.send(createErrorMessage(ErrorCodes.PERMISSION_DENIED, `Failed to write config: ${error}`));
|
|
645
774
|
}
|
|
646
775
|
}
|
|
776
|
+
async handleCreateFolder(relativePath, folderId) {
|
|
777
|
+
if (!this.syncFolder || !this.fileWatcher) return void this.send(createErrorMessage(ErrorCodes.FOLDER_NOT_FOUND, "No folder selected"));
|
|
778
|
+
const fs = await import("node:fs/promises");
|
|
779
|
+
const nodePath = await import("node:path");
|
|
780
|
+
const absolutePath = nodePath.join(this.syncFolder, relativePath);
|
|
781
|
+
try {
|
|
782
|
+
this.fileWatcher.markProcessing(relativePath);
|
|
783
|
+
await fs.mkdir(absolutePath, {
|
|
784
|
+
recursive: true
|
|
785
|
+
});
|
|
786
|
+
console.log(`[SyncServer] Folder created: ${relativePath}`);
|
|
787
|
+
await this.fileWatcher.writeConfig(relativePath, void 0, folderId);
|
|
788
|
+
this.fileWatcher.unmarkProcessing(relativePath);
|
|
789
|
+
this.send({
|
|
790
|
+
type: "config_written",
|
|
791
|
+
path: relativePath,
|
|
792
|
+
folderId
|
|
793
|
+
});
|
|
794
|
+
} catch (error) {
|
|
795
|
+
this.fileWatcher.unmarkProcessing(relativePath);
|
|
796
|
+
console.error(`[SyncServer] Failed to create folder: ${relativePath}`, error);
|
|
797
|
+
this.send(createErrorMessage(ErrorCodes.PERMISSION_DENIED, `Failed to create folder: ${error}`));
|
|
798
|
+
}
|
|
799
|
+
}
|
|
647
800
|
async handleCancelTransfer(requestId) {
|
|
648
801
|
if (!this.fileTransfer) return;
|
|
649
802
|
await this.fileTransfer.cancelTransfer(requestId);
|
|
@@ -677,7 +830,7 @@ class SyncServer {
|
|
|
677
830
|
}
|
|
678
831
|
const program = new Command();
|
|
679
832
|
program.name("syncast-cli").description("Syncast CLI tool for local file sync").version("0.1.0");
|
|
680
|
-
program.command("sync").description("Start the local sync server").option("-p, --port <port>", "Port to listen on", String(
|
|
833
|
+
program.command("sync").description("Start the local sync server").option("-p, --port <port>", "Port to listen on", String(__webpack_exports__DEFAULT_PORT)).option("-H, --host <host>", "Host to bind to", DEFAULT_HOST).action(startSyncServer);
|
|
681
834
|
async function startSyncServer(options) {
|
|
682
835
|
const port = Number.parseInt(options.port, 10);
|
|
683
836
|
const host = options.host;
|
|
@@ -724,8 +877,8 @@ async function startSyncServer(options) {
|
|
|
724
877
|
process.on("SIGTERM", shutdown);
|
|
725
878
|
await new Promise(()=>{});
|
|
726
879
|
}
|
|
727
|
-
program.command("start").description("Alias for 'sync' command - Start the local sync server").option("-p, --port <port>", "Port to listen on", String(
|
|
728
|
-
program.command("status").description("Check if a sync server is running").option("-p, --port <port>", "Port to check", String(
|
|
880
|
+
program.command("start").description("Alias for 'sync' command - Start the local sync server").option("-p, --port <port>", "Port to listen on", String(__webpack_exports__DEFAULT_PORT)).option("-H, --host <host>", "Host to bind to", DEFAULT_HOST).action(startSyncServer);
|
|
881
|
+
program.command("status").description("Check if a sync server is running").option("-p, --port <port>", "Port to check", String(__webpack_exports__DEFAULT_PORT)).option("-H, --host <host>", "Host to check", DEFAULT_HOST).action(async (options)=>{
|
|
729
882
|
const port = Number.parseInt(options.port, 10);
|
|
730
883
|
const host = options.host;
|
|
731
884
|
const address = `ws://${host}:${port}`;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CHUNK_SIZE, DEFAULT_HOST, DEFAULT_PORT, ErrorCodes, FILE_CHANGE_DEBOUNCE_MS, FILE_STABLE_CHECK_INTERVAL_MS, PROTOCOL_VERSION, createConnectedMessage, createErrorMessage, createFileConfig, createFolderConfig, getFileConfigPath, getFilename, getFolderConfigPath, isConfigFile, isSupportedFile, parseFileConfig, parseFolderConfig, toRelativePath } from "local-sync-protocol";
|
|
2
1
|
import { WebSocketServer } from "ws";
|
|
3
2
|
import { mkdir, readFile, readdir, stat as promises_stat, writeFile } from "node:fs/promises";
|
|
4
3
|
import { dirname as external_node_path_dirname, join } from "node:path";
|
|
@@ -6,6 +5,134 @@ import { exec } from "node:child_process";
|
|
|
6
5
|
import { promisify } from "node:util";
|
|
7
6
|
import { watch } from "chokidar";
|
|
8
7
|
import { Command } from "commander";
|
|
8
|
+
const DEFAULT_HOST = "localhost";
|
|
9
|
+
const CHUNK_SIZE = 1048576;
|
|
10
|
+
const FILE_CONFIG_SUFFIX = ".config.json";
|
|
11
|
+
const FOLDER_CONFIG_SUFFIX = ".folder.config.json";
|
|
12
|
+
const SUPPORTED_EXTENSIONS = [
|
|
13
|
+
"jpg",
|
|
14
|
+
"jpeg",
|
|
15
|
+
"png",
|
|
16
|
+
"webp",
|
|
17
|
+
"gif",
|
|
18
|
+
"mp4",
|
|
19
|
+
"mov",
|
|
20
|
+
"m4v",
|
|
21
|
+
"webm",
|
|
22
|
+
"mp3",
|
|
23
|
+
"wav",
|
|
24
|
+
"ogg",
|
|
25
|
+
"m4a"
|
|
26
|
+
];
|
|
27
|
+
function createFileConfig(assetId, originalFilename, md5) {
|
|
28
|
+
return {
|
|
29
|
+
assetId,
|
|
30
|
+
syncedAt: new Date().toISOString(),
|
|
31
|
+
originalFilename,
|
|
32
|
+
md5
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function createFolderConfig(folderId) {
|
|
36
|
+
return {
|
|
37
|
+
folderId,
|
|
38
|
+
syncedAt: new Date().toISOString()
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function parseFileConfig(content) {
|
|
42
|
+
try {
|
|
43
|
+
const config = JSON.parse(content);
|
|
44
|
+
if (!config.assetId || "string" != typeof config.assetId) {
|
|
45
|
+
console.warn("Invalid config.json: missing or invalid assetId");
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (!config.originalFilename || "string" != typeof config.originalFilename) {
|
|
49
|
+
console.warn("Invalid config.json: missing or invalid originalFilename");
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return config;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.warn("Failed to parse config.json:", e);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function parseFolderConfig(content) {
|
|
59
|
+
try {
|
|
60
|
+
const config = JSON.parse(content);
|
|
61
|
+
if (!config.folderId || "string" != typeof config.folderId) {
|
|
62
|
+
console.warn("Invalid folder config.json: missing or invalid folderId");
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return config;
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.warn("Failed to parse folder config.json:", e);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function getFileConfigPath(filePath) {
|
|
72
|
+
return `${filePath}${FILE_CONFIG_SUFFIX}`;
|
|
73
|
+
}
|
|
74
|
+
function getFolderConfigPath(folderPath) {
|
|
75
|
+
const normalizedPath = folderPath.replace(/\/+$/, "");
|
|
76
|
+
return `${normalizedPath}${FOLDER_CONFIG_SUFFIX}`;
|
|
77
|
+
}
|
|
78
|
+
function isConfigFile(path) {
|
|
79
|
+
return path.endsWith(FOLDER_CONFIG_SUFFIX) || path.endsWith(FILE_CONFIG_SUFFIX);
|
|
80
|
+
}
|
|
81
|
+
function getFileExtension(filename) {
|
|
82
|
+
const lastDot = filename.lastIndexOf(".");
|
|
83
|
+
if (-1 === lastDot || lastDot === filename.length - 1) return "";
|
|
84
|
+
return filename.slice(lastDot + 1).toLowerCase();
|
|
85
|
+
}
|
|
86
|
+
function isSupportedFile(filename) {
|
|
87
|
+
const ext = getFileExtension(filename);
|
|
88
|
+
return ext ? SUPPORTED_EXTENSIONS.includes(ext) : false;
|
|
89
|
+
}
|
|
90
|
+
function getFilename(path) {
|
|
91
|
+
const normalized = path.replace(/\\/g, "/");
|
|
92
|
+
const parts = normalized.split("/");
|
|
93
|
+
return parts[parts.length - 1] || "";
|
|
94
|
+
}
|
|
95
|
+
function normalizePath(path) {
|
|
96
|
+
return path.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
97
|
+
}
|
|
98
|
+
function toRelativePath(absolutePath, basePath) {
|
|
99
|
+
const normalizedAbs = normalizePath(absolutePath);
|
|
100
|
+
const normalizedBase = normalizePath(basePath);
|
|
101
|
+
if (!normalizedAbs.startsWith(normalizedBase)) return normalizedAbs;
|
|
102
|
+
let relative = normalizedAbs.slice(normalizedBase.length);
|
|
103
|
+
if (relative.startsWith("/")) relative = relative.slice(1);
|
|
104
|
+
return relative;
|
|
105
|
+
}
|
|
106
|
+
function createConnectedMessage(cwd) {
|
|
107
|
+
return {
|
|
108
|
+
type: "connected",
|
|
109
|
+
cwd,
|
|
110
|
+
version: "1.0.0"
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function createErrorMessage(code, message, requestId) {
|
|
114
|
+
return {
|
|
115
|
+
type: "error",
|
|
116
|
+
code,
|
|
117
|
+
message,
|
|
118
|
+
requestId
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const ErrorCodes = {
|
|
122
|
+
FILE_NOT_FOUND: "FILE_NOT_FOUND",
|
|
123
|
+
FOLDER_NOT_FOUND: "FOLDER_NOT_FOUND",
|
|
124
|
+
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
125
|
+
FILE_IN_USE: "FILE_IN_USE",
|
|
126
|
+
TRANSFER_CANCELLED: "TRANSFER_CANCELLED",
|
|
127
|
+
INVALID_REQUEST_ID: "INVALID_REQUEST_ID",
|
|
128
|
+
VERSION_MISMATCH: "VERSION_MISMATCH",
|
|
129
|
+
PROJECT_ALREADY_CONNECTED: "PROJECT_ALREADY_CONNECTED",
|
|
130
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR"
|
|
131
|
+
};
|
|
132
|
+
var __webpack_exports__DEFAULT_PORT = 23456;
|
|
133
|
+
var __webpack_exports__FILE_CHANGE_DEBOUNCE_MS = 300;
|
|
134
|
+
var __webpack_exports__FILE_STABLE_CHECK_INTERVAL_MS = 500;
|
|
135
|
+
var __webpack_exports__PROTOCOL_VERSION = "1.0.0";
|
|
9
136
|
class FileTransferManager {
|
|
10
137
|
rootPath;
|
|
11
138
|
events;
|
|
@@ -135,7 +262,7 @@ class FileChangeCollector {
|
|
|
135
262
|
this.timer = setTimeout(()=>{
|
|
136
263
|
this.flush();
|
|
137
264
|
this.timer = null;
|
|
138
|
-
},
|
|
265
|
+
}, __webpack_exports__FILE_CHANGE_DEBOUNCE_MS);
|
|
139
266
|
}
|
|
140
267
|
flush() {
|
|
141
268
|
if (0 === this.pending.size) return;
|
|
@@ -251,7 +378,7 @@ class FileWatcher {
|
|
|
251
378
|
persistent: true,
|
|
252
379
|
ignoreInitial: true,
|
|
253
380
|
awaitWriteFinish: {
|
|
254
|
-
stabilityThreshold:
|
|
381
|
+
stabilityThreshold: __webpack_exports__FILE_STABLE_CHECK_INTERVAL_MS,
|
|
255
382
|
pollInterval: 100
|
|
256
383
|
}
|
|
257
384
|
});
|
|
@@ -358,7 +485,7 @@ class SyncServer {
|
|
|
358
485
|
isProcessingQueue = false;
|
|
359
486
|
currentProjectId = null;
|
|
360
487
|
constructor(options = {}, events = {}){
|
|
361
|
-
this.port = options.port ??
|
|
488
|
+
this.port = options.port ?? __webpack_exports__DEFAULT_PORT;
|
|
362
489
|
this.host = options.host ?? DEFAULT_HOST;
|
|
363
490
|
this.cwd = process.cwd();
|
|
364
491
|
this.events = events;
|
|
@@ -498,6 +625,9 @@ class SyncServer {
|
|
|
498
625
|
case "write_config":
|
|
499
626
|
await this.handleWriteConfig(message.path, message.assetId, message.folderId, message.originalFilename, message.md5);
|
|
500
627
|
break;
|
|
628
|
+
case "create_folder":
|
|
629
|
+
await this.handleCreateFolder(message.path, message.folderId);
|
|
630
|
+
break;
|
|
501
631
|
case "cancel_transfer":
|
|
502
632
|
await this.handleCancelTransfer(message.requestId);
|
|
503
633
|
break;
|
|
@@ -643,6 +773,30 @@ class SyncServer {
|
|
|
643
773
|
this.send(createErrorMessage(ErrorCodes.PERMISSION_DENIED, `Failed to write config: ${error}`));
|
|
644
774
|
}
|
|
645
775
|
}
|
|
776
|
+
async handleCreateFolder(relativePath, folderId) {
|
|
777
|
+
if (!this.syncFolder || !this.fileWatcher) return void this.send(createErrorMessage(ErrorCodes.FOLDER_NOT_FOUND, "No folder selected"));
|
|
778
|
+
const fs = await import("node:fs/promises");
|
|
779
|
+
const nodePath = await import("node:path");
|
|
780
|
+
const absolutePath = nodePath.join(this.syncFolder, relativePath);
|
|
781
|
+
try {
|
|
782
|
+
this.fileWatcher.markProcessing(relativePath);
|
|
783
|
+
await fs.mkdir(absolutePath, {
|
|
784
|
+
recursive: true
|
|
785
|
+
});
|
|
786
|
+
console.log(`[SyncServer] Folder created: ${relativePath}`);
|
|
787
|
+
await this.fileWatcher.writeConfig(relativePath, void 0, folderId);
|
|
788
|
+
this.fileWatcher.unmarkProcessing(relativePath);
|
|
789
|
+
this.send({
|
|
790
|
+
type: "config_written",
|
|
791
|
+
path: relativePath,
|
|
792
|
+
folderId
|
|
793
|
+
});
|
|
794
|
+
} catch (error) {
|
|
795
|
+
this.fileWatcher.unmarkProcessing(relativePath);
|
|
796
|
+
console.error(`[SyncServer] Failed to create folder: ${relativePath}`, error);
|
|
797
|
+
this.send(createErrorMessage(ErrorCodes.PERMISSION_DENIED, `Failed to create folder: ${error}`));
|
|
798
|
+
}
|
|
799
|
+
}
|
|
646
800
|
async handleCancelTransfer(requestId) {
|
|
647
801
|
if (!this.fileTransfer) return;
|
|
648
802
|
await this.fileTransfer.cancelTransfer(requestId);
|
|
@@ -676,7 +830,7 @@ class SyncServer {
|
|
|
676
830
|
}
|
|
677
831
|
const program = new Command();
|
|
678
832
|
program.name("syncast-cli").description("Syncast CLI tool for local file sync").version("0.1.0");
|
|
679
|
-
program.command("sync").description("Start the local sync server").option("-p, --port <port>", "Port to listen on", String(
|
|
833
|
+
program.command("sync").description("Start the local sync server").option("-p, --port <port>", "Port to listen on", String(__webpack_exports__DEFAULT_PORT)).option("-H, --host <host>", "Host to bind to", DEFAULT_HOST).action(startSyncServer);
|
|
680
834
|
async function startSyncServer(options) {
|
|
681
835
|
const port = Number.parseInt(options.port, 10);
|
|
682
836
|
const host = options.host;
|
|
@@ -723,8 +877,8 @@ async function startSyncServer(options) {
|
|
|
723
877
|
process.on("SIGTERM", shutdown);
|
|
724
878
|
await new Promise(()=>{});
|
|
725
879
|
}
|
|
726
|
-
program.command("start").description("Alias for 'sync' command - Start the local sync server").option("-p, --port <port>", "Port to listen on", String(
|
|
727
|
-
program.command("status").description("Check if a sync server is running").option("-p, --port <port>", "Port to check", String(
|
|
880
|
+
program.command("start").description("Alias for 'sync' command - Start the local sync server").option("-p, --port <port>", "Port to listen on", String(__webpack_exports__DEFAULT_PORT)).option("-H, --host <host>", "Host to bind to", DEFAULT_HOST).action(startSyncServer);
|
|
881
|
+
program.command("status").description("Check if a sync server is running").option("-p, --port <port>", "Port to check", String(__webpack_exports__DEFAULT_PORT)).option("-H, --host <host>", "Host to check", DEFAULT_HOST).action(async (options)=>{
|
|
728
882
|
const port = Number.parseInt(options.port, 10);
|
|
729
883
|
const host = options.host;
|
|
730
884
|
const address = `ws://${host}:${port}`;
|
|
@@ -757,4 +911,4 @@ async function runCli(args = process.argv) {
|
|
|
757
911
|
await program.parseAsync(args);
|
|
758
912
|
}
|
|
759
913
|
runCli();
|
|
760
|
-
export { DEFAULT_HOST, DEFAULT_PORT, FileTransferManager, FileWatcher, PROTOCOL_VERSION, SyncServer, runCli };
|
|
914
|
+
export { DEFAULT_HOST, __webpack_exports__DEFAULT_PORT as DEFAULT_PORT, FileTransferManager, FileWatcher, __webpack_exports__PROTOCOL_VERSION as PROTOCOL_VERSION, SyncServer, runCli };
|
package/dist/ws-server.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncast/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Syncast CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"chokidar": "^4.0.3",
|
|
36
36
|
"commander": "^13.1.0",
|
|
37
|
-
"local-sync-protocol": "workspace:*",
|
|
38
37
|
"mime-types": "^2.1.35",
|
|
39
38
|
"ws": "^8.18.0"
|
|
40
39
|
},
|
|
@@ -44,6 +43,7 @@
|
|
|
44
43
|
"@types/mime-types": "^2.1.4",
|
|
45
44
|
"@types/node": "^22.18.6",
|
|
46
45
|
"@types/ws": "^8.5.13",
|
|
46
|
+
"local-sync-protocol": "workspace:^",
|
|
47
47
|
"typescript": "^5.9.2"
|
|
48
48
|
}
|
|
49
49
|
}
|