@involvex/syncstuff-cli 0.0.1
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/.eslintignore +1 -0
- package/.prettierrc +12 -0
- package/Readme.md +98 -0
- package/bunfig.toml +26 -0
- package/eslint.config.ts +61 -0
- package/package.json +49 -0
- package/src/cli/commands/debug.ts +9 -0
- package/src/cli/commands/device.ts +224 -0
- package/src/cli/commands/devices.ts +78 -0
- package/src/cli/commands/help.ts +159 -0
- package/src/cli/commands/index.ts +0 -0
- package/src/cli/commands/login.ts +69 -0
- package/src/cli/commands/logout.ts +23 -0
- package/src/cli/commands/transfer.ts +148 -0
- package/src/cli/commands/version.ts +35 -0
- package/src/cli/commands/whoami.ts +63 -0
- package/src/cli/index.ts +86 -0
- package/src/core.ts +13 -0
- package/src/utils/api-client.ts +174 -0
- package/src/utils/config.ts +41 -0
- package/src/utils/context.ts +68 -0
- package/src/utils/ui.ts +83 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createBox, printHeader, printSeparator } from "../../utils/ui.js";
|
|
3
|
+
|
|
4
|
+
const commandHelp: Record<string, string> = {
|
|
5
|
+
login: `${chalk.cyan.bold("login")}
|
|
6
|
+
|
|
7
|
+
Log in to your Syncstuff account.
|
|
8
|
+
|
|
9
|
+
${chalk.bold("Usage:")}
|
|
10
|
+
syncstuff login
|
|
11
|
+
|
|
12
|
+
${chalk.bold("Description:")}
|
|
13
|
+
Authenticates with the Syncstuff API using your email and password.
|
|
14
|
+
Your credentials are stored securely for future sessions.`,
|
|
15
|
+
|
|
16
|
+
logout: `${chalk.cyan.bold("logout")}
|
|
17
|
+
|
|
18
|
+
Log out from your Syncstuff account.
|
|
19
|
+
|
|
20
|
+
${chalk.bold("Usage:")}
|
|
21
|
+
syncstuff logout
|
|
22
|
+
|
|
23
|
+
${chalk.bold("Description:")}
|
|
24
|
+
Clears your stored authentication credentials.`,
|
|
25
|
+
|
|
26
|
+
whoami: `${chalk.cyan.bold("whoami")}
|
|
27
|
+
|
|
28
|
+
Display your user profile.
|
|
29
|
+
|
|
30
|
+
${chalk.bold("Usage:")}
|
|
31
|
+
syncstuff whoami
|
|
32
|
+
|
|
33
|
+
${chalk.bold("Description:")}
|
|
34
|
+
Shows information about the currently logged-in user.`,
|
|
35
|
+
|
|
36
|
+
devices: `${chalk.cyan.bold("devices")}
|
|
37
|
+
|
|
38
|
+
List all your connected devices.
|
|
39
|
+
|
|
40
|
+
${chalk.bold("Usage:")}
|
|
41
|
+
syncstuff devices
|
|
42
|
+
|
|
43
|
+
${chalk.bold("Description:")}
|
|
44
|
+
Displays a table of all devices registered to your account,
|
|
45
|
+
including their status, platform, and last seen time.`,
|
|
46
|
+
|
|
47
|
+
device: `${chalk.cyan.bold("device")}
|
|
48
|
+
|
|
49
|
+
Connect to a specific device or list available devices.
|
|
50
|
+
|
|
51
|
+
${chalk.bold("Usage:")}
|
|
52
|
+
syncstuff device [options] [deviceId]
|
|
53
|
+
|
|
54
|
+
${chalk.bold("Options:")}
|
|
55
|
+
--list, -l List all available devices
|
|
56
|
+
|
|
57
|
+
${chalk.bold("Examples:")}
|
|
58
|
+
syncstuff device --list List all devices
|
|
59
|
+
syncstuff device Interactive device selection
|
|
60
|
+
syncstuff device abc123 Connect to device with ID abc123`,
|
|
61
|
+
|
|
62
|
+
transfer: `${chalk.cyan.bold("transfer")}
|
|
63
|
+
|
|
64
|
+
Transfer a file to a connected device.
|
|
65
|
+
|
|
66
|
+
${chalk.bold("Usage:")}
|
|
67
|
+
syncstuff transfer <file>
|
|
68
|
+
|
|
69
|
+
${chalk.bold("Description:")}
|
|
70
|
+
Sends the specified file to a connected device.
|
|
71
|
+
You must be connected to a device first.`,
|
|
72
|
+
|
|
73
|
+
version: `${chalk.cyan.bold("version")}
|
|
74
|
+
|
|
75
|
+
Show CLI version.
|
|
76
|
+
|
|
77
|
+
${chalk.bold("Usage:")}
|
|
78
|
+
syncstuff version
|
|
79
|
+
syncstuff --version
|
|
80
|
+
syncstuff -v`,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export async function showHelp(command?: string): Promise<void> {
|
|
84
|
+
printHeader();
|
|
85
|
+
|
|
86
|
+
// Show command-specific help if requested
|
|
87
|
+
if (command && commandHelp[command]) {
|
|
88
|
+
const helpContent = createBox(commandHelp[command], {
|
|
89
|
+
title: `Help: ${command}`,
|
|
90
|
+
titleAlignment: "center",
|
|
91
|
+
padding: 1,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
console.log(helpContent);
|
|
95
|
+
printSeparator();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Show general help
|
|
100
|
+
const helpContent = createBox(
|
|
101
|
+
chalk.cyan.bold("Available Commands\n\n") +
|
|
102
|
+
chalk.bold("Authentication:\n") +
|
|
103
|
+
" " +
|
|
104
|
+
chalk.green("login") +
|
|
105
|
+
" Login to your Syncstuff account\n" +
|
|
106
|
+
" " +
|
|
107
|
+
chalk.green("logout") +
|
|
108
|
+
" Logout from your account\n" +
|
|
109
|
+
" " +
|
|
110
|
+
chalk.green("whoami") +
|
|
111
|
+
" Display your user profile\n\n" +
|
|
112
|
+
chalk.bold("Device Management:\n") +
|
|
113
|
+
" " +
|
|
114
|
+
chalk.green("devices") +
|
|
115
|
+
" List all your connected devices\n" +
|
|
116
|
+
" " +
|
|
117
|
+
chalk.green("device --list") +
|
|
118
|
+
" List available devices\n" +
|
|
119
|
+
" " +
|
|
120
|
+
chalk.green("device <id>") +
|
|
121
|
+
" Connect to a specific device\n" +
|
|
122
|
+
" " +
|
|
123
|
+
chalk.green("transfer <file>") +
|
|
124
|
+
" Transfer a file to a device\n\n" +
|
|
125
|
+
chalk.bold("General:\n") +
|
|
126
|
+
" " +
|
|
127
|
+
chalk.green("help [command]") +
|
|
128
|
+
" Show help (or command-specific help)\n" +
|
|
129
|
+
" " +
|
|
130
|
+
chalk.green("version") +
|
|
131
|
+
" Show CLI version\n\n" +
|
|
132
|
+
chalk.bold("Global Flags:\n") +
|
|
133
|
+
" " +
|
|
134
|
+
chalk.yellow("-h, --help") +
|
|
135
|
+
" Show help for any command\n" +
|
|
136
|
+
" " +
|
|
137
|
+
chalk.yellow("-d, --debug") +
|
|
138
|
+
" Enable debug mode\n\n" +
|
|
139
|
+
chalk.gray("Examples:\n") +
|
|
140
|
+
" syncstuff devices -d " +
|
|
141
|
+
chalk.gray("List devices with debug output\n") +
|
|
142
|
+
" syncstuff device -h " +
|
|
143
|
+
chalk.gray("Show help for device command"),
|
|
144
|
+
{
|
|
145
|
+
title: "Syncstuff CLI Help",
|
|
146
|
+
titleAlignment: "center",
|
|
147
|
+
padding: 1,
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
console.log(helpContent);
|
|
152
|
+
printSeparator();
|
|
153
|
+
console.log(
|
|
154
|
+
chalk.gray(
|
|
155
|
+
"For more information, visit: https://syncstuff-web.involvex.workers.dev/",
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
printSeparator();
|
|
159
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { apiClient } from "../../utils/api-client.js";
|
|
3
|
+
import { debugLog, type CommandContext } from "../../utils/context.js";
|
|
4
|
+
import {
|
|
5
|
+
createSpinner,
|
|
6
|
+
error,
|
|
7
|
+
info,
|
|
8
|
+
printHeader,
|
|
9
|
+
printSeparator,
|
|
10
|
+
success,
|
|
11
|
+
} from "../../utils/ui.js";
|
|
12
|
+
|
|
13
|
+
export async function login(ctx: CommandContext): Promise<void> {
|
|
14
|
+
printHeader();
|
|
15
|
+
debugLog(ctx, "Starting login flow");
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const answers = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
type: "input",
|
|
21
|
+
name: "email",
|
|
22
|
+
message: "Email address:",
|
|
23
|
+
validate: (input: string) => {
|
|
24
|
+
if (!input || !input.includes("@")) {
|
|
25
|
+
return "Please enter a valid email address";
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "password",
|
|
32
|
+
name: "password",
|
|
33
|
+
message: "Password:",
|
|
34
|
+
mask: "*",
|
|
35
|
+
validate: (input: string) => {
|
|
36
|
+
if (!input || input.length < 1) {
|
|
37
|
+
return "Password cannot be empty";
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const spinner = createSpinner("Logging in...");
|
|
45
|
+
spinner.start();
|
|
46
|
+
|
|
47
|
+
const response = await apiClient.login(answers.email, answers.password);
|
|
48
|
+
|
|
49
|
+
if (response.success && response.data) {
|
|
50
|
+
spinner.succeed("Login successful!");
|
|
51
|
+
printSeparator();
|
|
52
|
+
success(
|
|
53
|
+
`Welcome, ${response.data.user.username || response.data.user.email}!`,
|
|
54
|
+
);
|
|
55
|
+
info(`Role: ${response.data.user.role}`);
|
|
56
|
+
if (response.data.user.full_name) {
|
|
57
|
+
info(`Name: ${response.data.user.full_name}`);
|
|
58
|
+
}
|
|
59
|
+
printSeparator();
|
|
60
|
+
} else {
|
|
61
|
+
spinner.fail("Login failed");
|
|
62
|
+
error(response.error || "Invalid credentials");
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
error(`Login error: ${err instanceof Error ? err.message : String(err)}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { apiClient } from "../../utils/api-client.js";
|
|
2
|
+
import { debugLog, type CommandContext } from "../../utils/context.js";
|
|
3
|
+
import { error, printHeader, printSeparator, success } from "../../utils/ui.js";
|
|
4
|
+
|
|
5
|
+
export async function logout(ctx: CommandContext): Promise<void> {
|
|
6
|
+
printHeader();
|
|
7
|
+
debugLog(ctx, "Starting logout flow");
|
|
8
|
+
|
|
9
|
+
if (!apiClient.isAuthenticated()) {
|
|
10
|
+
error("You are not logged in");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
await apiClient.logout();
|
|
16
|
+
printSeparator();
|
|
17
|
+
success("Logged out successfully");
|
|
18
|
+
printSeparator();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
error(`Logout error: ${err instanceof Error ? err.message : String(err)}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { existsSync, statSync } from "fs";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
import { apiClient } from "../../utils/api-client.js";
|
|
6
|
+
import { debugLog, type CommandContext } from "../../utils/context.js";
|
|
7
|
+
import {
|
|
8
|
+
createSpinner,
|
|
9
|
+
error,
|
|
10
|
+
info,
|
|
11
|
+
printHeader,
|
|
12
|
+
printSeparator,
|
|
13
|
+
success,
|
|
14
|
+
} from "../../utils/ui.js";
|
|
15
|
+
|
|
16
|
+
export async function transferFile(
|
|
17
|
+
filePath: string | undefined,
|
|
18
|
+
ctx: CommandContext,
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
printHeader();
|
|
21
|
+
debugLog(ctx, "Starting file transfer", { filePath });
|
|
22
|
+
|
|
23
|
+
if (!apiClient.isAuthenticated()) {
|
|
24
|
+
error("You are not logged in. Please run 'syncstuff login' first.");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let targetFile = filePath;
|
|
29
|
+
|
|
30
|
+
// If no file path provided, prompt for it
|
|
31
|
+
if (!targetFile) {
|
|
32
|
+
const answers = await inquirer.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: "input",
|
|
35
|
+
name: "filePath",
|
|
36
|
+
message: "File path to transfer:",
|
|
37
|
+
validate: (input: string) => {
|
|
38
|
+
if (!input) {
|
|
39
|
+
return "Please enter a file path";
|
|
40
|
+
}
|
|
41
|
+
const resolved = resolve(input);
|
|
42
|
+
if (!existsSync(resolved)) {
|
|
43
|
+
return "File does not exist";
|
|
44
|
+
}
|
|
45
|
+
if (!statSync(resolved).isFile()) {
|
|
46
|
+
return "Path is not a file";
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
targetFile = answers.filePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!targetFile) {
|
|
56
|
+
error("No file path provided");
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const resolvedPath = resolve(targetFile);
|
|
61
|
+
|
|
62
|
+
if (!existsSync(resolvedPath)) {
|
|
63
|
+
error(`File not found: ${resolvedPath}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!statSync(resolvedPath).isFile()) {
|
|
68
|
+
error(`Path is not a file: ${resolvedPath}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get file info
|
|
73
|
+
const stats = statSync(resolvedPath);
|
|
74
|
+
const fileSize = (stats.size / 1024 / 1024).toFixed(2);
|
|
75
|
+
|
|
76
|
+
info(`File: ${resolvedPath}`);
|
|
77
|
+
info(`Size: ${fileSize} MB`);
|
|
78
|
+
|
|
79
|
+
// Get device list
|
|
80
|
+
const devicesSpinner = createSpinner("Fetching devices...");
|
|
81
|
+
devicesSpinner.start();
|
|
82
|
+
|
|
83
|
+
const devicesResponse = await apiClient.getDevices();
|
|
84
|
+
devicesSpinner.stop();
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
!devicesResponse.success ||
|
|
88
|
+
!devicesResponse.data ||
|
|
89
|
+
devicesResponse.data.length === 0
|
|
90
|
+
) {
|
|
91
|
+
error(
|
|
92
|
+
"No devices available. Please ensure at least one device is connected.",
|
|
93
|
+
);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Prompt for device selection
|
|
98
|
+
const deviceChoices = devicesResponse.data.map(device => ({
|
|
99
|
+
name: `${device.name} (${device.platform}) - ${device.is_online ? "Online" : "Offline"}`,
|
|
100
|
+
value: device.id,
|
|
101
|
+
disabled: !device.is_online ? "Offline" : false,
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
const deviceAnswer = await inquirer.prompt([
|
|
105
|
+
{
|
|
106
|
+
type: "list",
|
|
107
|
+
name: "deviceId",
|
|
108
|
+
message: "Select target device:",
|
|
109
|
+
choices: deviceChoices,
|
|
110
|
+
},
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
// Transfer file
|
|
114
|
+
const transferSpinner = createSpinner(
|
|
115
|
+
`Transferring ${chalk.cyan(resolvedPath)} to device...`,
|
|
116
|
+
);
|
|
117
|
+
transferSpinner.start();
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const transferResponse = await apiClient.transferFile(
|
|
121
|
+
deviceAnswer.deviceId,
|
|
122
|
+
resolvedPath,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (transferResponse.success) {
|
|
126
|
+
transferSpinner.succeed("File transfer initiated!");
|
|
127
|
+
printSeparator();
|
|
128
|
+
success(`Transfer ID: ${transferResponse.data?.transferId || "N/A"}`);
|
|
129
|
+
info("File is being synced to the target device");
|
|
130
|
+
printSeparator();
|
|
131
|
+
} else {
|
|
132
|
+
transferSpinner.fail("Transfer failed");
|
|
133
|
+
if (
|
|
134
|
+
transferResponse.error?.includes("404") ||
|
|
135
|
+
transferResponse.error?.includes("Not found")
|
|
136
|
+
) {
|
|
137
|
+
info("File transfer endpoint not yet implemented in API");
|
|
138
|
+
info("This feature will be available soon!");
|
|
139
|
+
} else {
|
|
140
|
+
error(transferResponse.error || "Unknown error");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
transferSpinner.fail("Transfer error");
|
|
145
|
+
error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { createBox, printHeader, printSeparator } from "../../utils/ui.js";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
export function showversion() {
|
|
11
|
+
printHeader();
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const packagePath = join(__dirname, "../../../package.json");
|
|
15
|
+
const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
16
|
+
const version = packageJson.version || "0.0.1";
|
|
17
|
+
|
|
18
|
+
const versionBox = createBox(
|
|
19
|
+
chalk.cyan.bold("Syncstuff CLI\n\n") +
|
|
20
|
+
chalk.bold("Version:") +
|
|
21
|
+
` ${chalk.green(version)}\n` +
|
|
22
|
+
chalk.bold("Package:") +
|
|
23
|
+
` @involvex/syncstuff-cli`,
|
|
24
|
+
{
|
|
25
|
+
title: "Version Information",
|
|
26
|
+
titleAlignment: "center",
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
console.log(versionBox);
|
|
31
|
+
printSeparator();
|
|
32
|
+
} catch {
|
|
33
|
+
console.log(chalk.yellow("Version: 0.0.1 (unable to read package.json)"));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { apiClient } from "../../utils/api-client.js";
|
|
3
|
+
import { debugLog, type CommandContext } from "../../utils/context.js";
|
|
4
|
+
import {
|
|
5
|
+
createBox,
|
|
6
|
+
createSpinner,
|
|
7
|
+
error,
|
|
8
|
+
printHeader,
|
|
9
|
+
printSeparator,
|
|
10
|
+
} from "../../utils/ui.js";
|
|
11
|
+
|
|
12
|
+
export async function whoami(ctx: CommandContext): Promise<void> {
|
|
13
|
+
printHeader();
|
|
14
|
+
debugLog(ctx, "Fetching user profile");
|
|
15
|
+
|
|
16
|
+
if (!apiClient.isAuthenticated()) {
|
|
17
|
+
error("You are not logged in. Please run 'syncstuff login' first.");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const spinner = createSpinner("Fetching user profile...");
|
|
22
|
+
spinner.start();
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const response = await apiClient.getProfile();
|
|
26
|
+
|
|
27
|
+
if (response.success && response.data) {
|
|
28
|
+
spinner.succeed("Profile loaded");
|
|
29
|
+
printSeparator();
|
|
30
|
+
|
|
31
|
+
const user = response.data;
|
|
32
|
+
|
|
33
|
+
const profileBox = createBox(
|
|
34
|
+
chalk.cyan.bold("User Profile\n\n") +
|
|
35
|
+
`${chalk.bold("ID:")} ${user.id}\n` +
|
|
36
|
+
`${chalk.bold("Email:")} ${user.email}\n` +
|
|
37
|
+
`${chalk.bold("Username:")} ${user.username}\n` +
|
|
38
|
+
(user.full_name
|
|
39
|
+
? `${chalk.bold("Full Name:")} ${user.full_name}\n`
|
|
40
|
+
: "") +
|
|
41
|
+
`${chalk.bold("Role:")} ${chalk.yellow(user.role)}\n` +
|
|
42
|
+
`${chalk.bold("Status:")} ${chalk.green(user.status)}\n` +
|
|
43
|
+
`${chalk.bold("Created:")} ${new Date(user.created_at).toLocaleString()}\n` +
|
|
44
|
+
`${chalk.bold("Updated:")} ${new Date(user.updated_at).toLocaleString()}`,
|
|
45
|
+
{
|
|
46
|
+
title: "Syncstuff Account",
|
|
47
|
+
titleAlignment: "center",
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
console.log(profileBox);
|
|
52
|
+
printSeparator();
|
|
53
|
+
} else {
|
|
54
|
+
spinner.fail("Failed to fetch profile");
|
|
55
|
+
error(response.error || "Unknown error");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
spinner.fail("Error fetching profile");
|
|
60
|
+
error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { debugLog, parseArgs, type CommandContext } from "../utils/context.js";
|
|
3
|
+
import { printHeader } from "../utils/ui.js";
|
|
4
|
+
|
|
5
|
+
export async function run() {
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const { command, flags, commandArgs } = parseArgs(args);
|
|
8
|
+
|
|
9
|
+
const ctx: CommandContext = { debug: flags.debug };
|
|
10
|
+
|
|
11
|
+
// Debug mode: log parsed arguments
|
|
12
|
+
debugLog(ctx, "Parsed arguments:", { command, flags, commandArgs });
|
|
13
|
+
|
|
14
|
+
// Handle help flag - show command-specific or general help
|
|
15
|
+
if (flags.help) {
|
|
16
|
+
const { showHelp } = await import("./commands/help.js");
|
|
17
|
+
await showHelp(command);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Handle no command
|
|
22
|
+
if (!command) {
|
|
23
|
+
const { showHelp } = await import("./commands/help.js");
|
|
24
|
+
await showHelp();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
switch (command) {
|
|
29
|
+
case "login":
|
|
30
|
+
{
|
|
31
|
+
const { login } = await import("./commands/login.js");
|
|
32
|
+
await login(ctx);
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
case "whoami":
|
|
36
|
+
{
|
|
37
|
+
const { whoami } = await import("./commands/whoami.js");
|
|
38
|
+
await whoami(ctx);
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
case "logout":
|
|
42
|
+
{
|
|
43
|
+
const { logout } = await import("./commands/logout.js");
|
|
44
|
+
await logout(ctx);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case "devices":
|
|
48
|
+
{
|
|
49
|
+
const { listDevices } = await import("./commands/devices.js");
|
|
50
|
+
await listDevices(ctx);
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
case "device":
|
|
54
|
+
{
|
|
55
|
+
const { device } = await import("./commands/device.js");
|
|
56
|
+
await device(commandArgs, ctx);
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
case "transfer":
|
|
60
|
+
{
|
|
61
|
+
const { transferFile } = await import("./commands/transfer.js");
|
|
62
|
+
await transferFile(commandArgs[0], ctx);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case "help":
|
|
66
|
+
{
|
|
67
|
+
const { showHelp } = await import("./commands/help.js");
|
|
68
|
+
await showHelp(commandArgs[0]);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
case "version":
|
|
72
|
+
case "--version":
|
|
73
|
+
case "-v": {
|
|
74
|
+
const { showversion } = await import("./commands/version.js");
|
|
75
|
+
showversion();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
default:
|
|
79
|
+
printHeader();
|
|
80
|
+
console.log(`❌ Unknown command: ${command}`);
|
|
81
|
+
console.log("Run 'syncstuff help' to see available commands");
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
run();
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Core types moved to utils/api-client.ts
|
|
2
|
+
export type { UserProfile as User } from "./utils/api-client.js";
|
|
3
|
+
|
|
4
|
+
export interface Device {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
platform: string;
|
|
8
|
+
is_online: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type DebugMode = {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
};
|