@involvex/syncstuff-cli 0.0.4 → 0.0.5
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 +108 -22
- package/package.json +1 -1
- package/src/cli/commands/devices.ts +71 -4
- package/src/cli/commands/version.ts +0 -1
- package/src/cli/index.ts +5 -1
- package/src/utils/update-checker.ts +86 -0
package/dist/cli.js
CHANGED
|
@@ -20411,7 +20411,7 @@ var init_RemoveFileError = __esm(() => {
|
|
|
20411
20411
|
|
|
20412
20412
|
// ../../node_modules/.bun/@inquirer+external-editor@2.0.2+cda980ff03d0f389/node_modules/@inquirer/external-editor/dist/index.js
|
|
20413
20413
|
import { spawn, spawnSync } from "child_process";
|
|
20414
|
-
import { readFileSync as
|
|
20414
|
+
import { readFileSync as readFileSync3, unlinkSync, writeFileSync } from "fs";
|
|
20415
20415
|
import path from "node:path";
|
|
20416
20416
|
import os2 from "node:os";
|
|
20417
20417
|
import { randomUUID } from "node:crypto";
|
|
@@ -20527,7 +20527,7 @@ class ExternalEditor {
|
|
|
20527
20527
|
}
|
|
20528
20528
|
readTemporaryFile() {
|
|
20529
20529
|
try {
|
|
20530
|
-
const tempFileBuffer =
|
|
20530
|
+
const tempFileBuffer = readFileSync3(this.tempFile);
|
|
20531
20531
|
if (tempFileBuffer.length === 0) {
|
|
20532
20532
|
this.text = "";
|
|
20533
20533
|
} else {
|
|
@@ -30801,9 +30801,9 @@ var init_dist16 = __esm(() => {
|
|
|
30801
30801
|
});
|
|
30802
30802
|
|
|
30803
30803
|
// src/utils/config.ts
|
|
30804
|
-
import { mkdirSync, readFileSync as
|
|
30804
|
+
import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
30805
30805
|
import { homedir } from "os";
|
|
30806
|
-
import { join as
|
|
30806
|
+
import { join as join3 } from "path";
|
|
30807
30807
|
function ensureConfigDir() {
|
|
30808
30808
|
try {
|
|
30809
30809
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
@@ -30812,7 +30812,7 @@ function ensureConfigDir() {
|
|
|
30812
30812
|
function readConfig() {
|
|
30813
30813
|
ensureConfigDir();
|
|
30814
30814
|
try {
|
|
30815
|
-
const content =
|
|
30815
|
+
const content = readFileSync4(CONFIG_FILE, "utf-8");
|
|
30816
30816
|
return JSON.parse(content);
|
|
30817
30817
|
} catch {
|
|
30818
30818
|
return {};
|
|
@@ -30824,8 +30824,8 @@ function writeConfig(config) {
|
|
|
30824
30824
|
}
|
|
30825
30825
|
var CONFIG_DIR, CONFIG_FILE;
|
|
30826
30826
|
var init_config = __esm(() => {
|
|
30827
|
-
CONFIG_DIR =
|
|
30828
|
-
CONFIG_FILE =
|
|
30827
|
+
CONFIG_DIR = join3(homedir(), ".syncstuff");
|
|
30828
|
+
CONFIG_FILE = join3(CONFIG_DIR, "config.json");
|
|
30829
30829
|
});
|
|
30830
30830
|
|
|
30831
30831
|
// src/utils/api-client.ts
|
|
@@ -31079,8 +31079,17 @@ var exports_devices = {};
|
|
|
31079
31079
|
__export(exports_devices, {
|
|
31080
31080
|
listDevices: () => listDevices
|
|
31081
31081
|
});
|
|
31082
|
-
async function listDevices(ctx) {
|
|
31083
|
-
|
|
31082
|
+
async function listDevices(args, ctx) {
|
|
31083
|
+
const isLoop = args.includes("--loop") || args.includes("-l");
|
|
31084
|
+
debugLog(ctx, "Devices command", { loop: isLoop });
|
|
31085
|
+
if (isLoop) {
|
|
31086
|
+
await loopDevices(ctx);
|
|
31087
|
+
} else {
|
|
31088
|
+
printHeader();
|
|
31089
|
+
await fetchAndDisplayDevices(ctx);
|
|
31090
|
+
}
|
|
31091
|
+
}
|
|
31092
|
+
async function fetchAndDisplayDevices(ctx) {
|
|
31084
31093
|
debugLog(ctx, "Fetching devices list");
|
|
31085
31094
|
if (!apiClient.isAuthenticated()) {
|
|
31086
31095
|
error2("You are not logged in. Please run 'syncstuff login' first.");
|
|
@@ -31096,7 +31105,7 @@ async function listDevices(ctx) {
|
|
|
31096
31105
|
if (response.data.length === 0) {
|
|
31097
31106
|
info2("No devices found. Connect a device to get started.");
|
|
31098
31107
|
printSeparator();
|
|
31099
|
-
return;
|
|
31108
|
+
return true;
|
|
31100
31109
|
}
|
|
31101
31110
|
const tableData = response.data.map((device) => [
|
|
31102
31111
|
device.id.substring(0, 8) + "...",
|
|
@@ -31118,6 +31127,7 @@ async function listDevices(ctx) {
|
|
|
31118
31127
|
printSeparator();
|
|
31119
31128
|
success2(`Found ${response.data.length} device(s)`);
|
|
31120
31129
|
printSeparator();
|
|
31130
|
+
return true;
|
|
31121
31131
|
} else {
|
|
31122
31132
|
spinner.fail("Failed to fetch devices");
|
|
31123
31133
|
if (response.error?.includes("404") || response.error?.includes("Not found")) {
|
|
@@ -31126,13 +31136,39 @@ async function listDevices(ctx) {
|
|
|
31126
31136
|
} else {
|
|
31127
31137
|
error2(response.error || "Unknown error");
|
|
31128
31138
|
}
|
|
31139
|
+
return false;
|
|
31129
31140
|
}
|
|
31130
31141
|
} catch (err) {
|
|
31131
31142
|
spinner.fail("Error fetching devices");
|
|
31132
31143
|
error2(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
31133
|
-
|
|
31144
|
+
return false;
|
|
31134
31145
|
}
|
|
31135
31146
|
}
|
|
31147
|
+
async function loopDevices(ctx) {
|
|
31148
|
+
const REFRESH_INTERVAL = 5000;
|
|
31149
|
+
console.clear();
|
|
31150
|
+
printHeader();
|
|
31151
|
+
console.log(source_default.cyan("Loop mode enabled. Press Ctrl+C to exit."));
|
|
31152
|
+
console.log(source_default.gray(`Refreshing every ${REFRESH_INTERVAL / 1000} seconds...
|
|
31153
|
+
`));
|
|
31154
|
+
process.on("SIGINT", () => {
|
|
31155
|
+
console.log(`
|
|
31156
|
+
`);
|
|
31157
|
+
info2("Loop stopped by user");
|
|
31158
|
+
process.exit(0);
|
|
31159
|
+
});
|
|
31160
|
+
await fetchAndDisplayDevices(ctx);
|
|
31161
|
+
const interval = setInterval(async () => {
|
|
31162
|
+
console.clear();
|
|
31163
|
+
printHeader();
|
|
31164
|
+
console.log(source_default.cyan("Loop mode enabled. Press Ctrl+C to exit."));
|
|
31165
|
+
console.log(source_default.gray(`Last refresh: ${new Date().toLocaleTimeString()}
|
|
31166
|
+
`));
|
|
31167
|
+
await fetchAndDisplayDevices(ctx);
|
|
31168
|
+
}, REFRESH_INTERVAL);
|
|
31169
|
+
await new Promise(() => {});
|
|
31170
|
+
clearInterval(interval);
|
|
31171
|
+
}
|
|
31136
31172
|
var init_devices = __esm(() => {
|
|
31137
31173
|
init_source();
|
|
31138
31174
|
init_api_client();
|
|
@@ -31473,14 +31509,14 @@ var exports_version = {};
|
|
|
31473
31509
|
__export(exports_version, {
|
|
31474
31510
|
showversion: () => showversion
|
|
31475
31511
|
});
|
|
31476
|
-
import { readFileSync as
|
|
31477
|
-
import { dirname, join as
|
|
31478
|
-
import { fileURLToPath } from "url";
|
|
31512
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
31513
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
31514
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31479
31515
|
function showversion() {
|
|
31480
31516
|
printHeader();
|
|
31481
31517
|
try {
|
|
31482
|
-
const packagePath =
|
|
31483
|
-
const packageJson = JSON.parse(
|
|
31518
|
+
const packagePath = join4(__dirname3, "../../../package.json");
|
|
31519
|
+
const packageJson = JSON.parse(readFileSync5(packagePath, "utf-8"));
|
|
31484
31520
|
const version = packageJson.version;
|
|
31485
31521
|
if (DebugMode.enabled === true) {
|
|
31486
31522
|
console.log(source_default.yellow("Debug mode enabled"));
|
|
@@ -31498,29 +31534,79 @@ function showversion() {
|
|
|
31498
31534
|
printSeparator();
|
|
31499
31535
|
} catch {
|
|
31500
31536
|
if (DebugMode.enabled === true) {
|
|
31501
|
-
const packagePath =
|
|
31537
|
+
const packagePath = join4(__dirname3, "../../../package.json");
|
|
31502
31538
|
console.log(source_default.yellow("Debug mode enabled"));
|
|
31503
31539
|
console.log("Packagepath: " + packagePath);
|
|
31504
31540
|
console.log(source_default.yellow("Version: 0.0.1 (unable to read package.json)"));
|
|
31505
31541
|
}
|
|
31506
|
-
console.log(source_default.yellow("Version: 0.0.1 (unable to read package.json)"));
|
|
31507
31542
|
}
|
|
31508
31543
|
}
|
|
31509
|
-
var
|
|
31544
|
+
var __filename3, __dirname3;
|
|
31510
31545
|
var init_version = __esm(() => {
|
|
31511
31546
|
init_source();
|
|
31512
31547
|
init_core();
|
|
31513
31548
|
init_ui();
|
|
31514
|
-
|
|
31515
|
-
|
|
31549
|
+
__filename3 = fileURLToPath2(import.meta.url);
|
|
31550
|
+
__dirname3 = dirname2(__filename3);
|
|
31516
31551
|
});
|
|
31517
31552
|
|
|
31518
31553
|
// src/cli/index.ts
|
|
31519
31554
|
init_ui();
|
|
31555
|
+
|
|
31556
|
+
// src/utils/update-checker.ts
|
|
31557
|
+
init_source();
|
|
31558
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
31559
|
+
import { dirname, join as join2 } from "path";
|
|
31560
|
+
import { fileURLToPath } from "url";
|
|
31561
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
31562
|
+
var __dirname2 = dirname(__filename2);
|
|
31563
|
+
async function checkForUpdates() {
|
|
31564
|
+
try {
|
|
31565
|
+
const packagePath = join2(__dirname2, "../../package.json");
|
|
31566
|
+
const packageJson = JSON.parse(readFileSync2(packagePath, "utf-8"));
|
|
31567
|
+
const currentVersion = packageJson.version;
|
|
31568
|
+
const packageName = packageJson.name;
|
|
31569
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
31570
|
+
headers: { Accept: "application/json" },
|
|
31571
|
+
signal: AbortSignal.timeout(3000)
|
|
31572
|
+
});
|
|
31573
|
+
if (!response.ok) {
|
|
31574
|
+
return;
|
|
31575
|
+
}
|
|
31576
|
+
const data = await response.json();
|
|
31577
|
+
const latestVersion = data.version;
|
|
31578
|
+
if (latestVersion && latestVersion !== currentVersion) {
|
|
31579
|
+
const isNewer = compareVersions(latestVersion, currentVersion) > 0;
|
|
31580
|
+
if (isNewer) {
|
|
31581
|
+
console.log("");
|
|
31582
|
+
console.log(source_default.yellow.bold("╭─────────────────────────────────────────╮"));
|
|
31583
|
+
console.log(source_default.yellow.bold("│") + source_default.yellow(" Update available! ") + source_default.gray(`${currentVersion}`) + source_default.yellow(" → ") + source_default.green.bold(`${latestVersion}`) + source_default.yellow.bold(" │"));
|
|
31584
|
+
console.log(source_default.yellow.bold("│") + source_default.cyan(` Run: npm i -g ${packageName}`) + " ".repeat(7) + source_default.yellow.bold("│"));
|
|
31585
|
+
console.log(source_default.yellow.bold("╰─────────────────────────────────────────╯"));
|
|
31586
|
+
console.log("");
|
|
31587
|
+
}
|
|
31588
|
+
}
|
|
31589
|
+
} catch {}
|
|
31590
|
+
}
|
|
31591
|
+
function compareVersions(a, b) {
|
|
31592
|
+
const partsA = a.split(".").map(Number);
|
|
31593
|
+
const partsB = b.split(".").map(Number);
|
|
31594
|
+
for (let i = 0;i < 3; i++) {
|
|
31595
|
+
const partA = partsA[i] || 0;
|
|
31596
|
+
const partB = partsB[i] || 0;
|
|
31597
|
+
if (partA !== partB) {
|
|
31598
|
+
return partA - partB;
|
|
31599
|
+
}
|
|
31600
|
+
}
|
|
31601
|
+
return 0;
|
|
31602
|
+
}
|
|
31603
|
+
|
|
31604
|
+
// src/cli/index.ts
|
|
31520
31605
|
async function run() {
|
|
31521
31606
|
const args = process.argv.slice(2);
|
|
31522
31607
|
const { command, flags, commandArgs } = parseArgs(args);
|
|
31523
31608
|
const ctx = { debug: flags.debug };
|
|
31609
|
+
checkForUpdates();
|
|
31524
31610
|
debugLog(ctx, "Parsed arguments:", { command, flags, commandArgs });
|
|
31525
31611
|
if (flags.help) {
|
|
31526
31612
|
const { showHelp: showHelp2 } = await Promise.resolve().then(() => (init_help(), exports_help));
|
|
@@ -31554,7 +31640,7 @@ async function run() {
|
|
|
31554
31640
|
case "devices":
|
|
31555
31641
|
{
|
|
31556
31642
|
const { listDevices: listDevices2 } = await Promise.resolve().then(() => (init_devices(), exports_devices));
|
|
31557
|
-
await listDevices2(ctx);
|
|
31643
|
+
await listDevices2(commandArgs, ctx);
|
|
31558
31644
|
}
|
|
31559
31645
|
break;
|
|
31560
31646
|
case "device":
|
package/package.json
CHANGED
|
@@ -11,8 +11,31 @@ import {
|
|
|
11
11
|
success,
|
|
12
12
|
} from "../../utils/ui.js";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* List devices command
|
|
16
|
+
* Options:
|
|
17
|
+
* --loop Continuously refresh devices until Ctrl+C
|
|
18
|
+
*/
|
|
19
|
+
export async function listDevices(
|
|
20
|
+
args: string[],
|
|
21
|
+
ctx: CommandContext,
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
const isLoop = args.includes("--loop") || args.includes("-l");
|
|
24
|
+
|
|
25
|
+
debugLog(ctx, "Devices command", { loop: isLoop });
|
|
26
|
+
|
|
27
|
+
if (isLoop) {
|
|
28
|
+
await loopDevices(ctx);
|
|
29
|
+
} else {
|
|
30
|
+
printHeader();
|
|
31
|
+
await fetchAndDisplayDevices(ctx);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Fetch and display devices once
|
|
37
|
+
*/
|
|
38
|
+
async function fetchAndDisplayDevices(ctx: CommandContext): Promise<boolean> {
|
|
16
39
|
debugLog(ctx, "Fetching devices list");
|
|
17
40
|
|
|
18
41
|
if (!apiClient.isAuthenticated()) {
|
|
@@ -33,7 +56,7 @@ export async function listDevices(ctx: CommandContext): Promise<void> {
|
|
|
33
56
|
if (response.data.length === 0) {
|
|
34
57
|
info("No devices found. Connect a device to get started.");
|
|
35
58
|
printSeparator();
|
|
36
|
-
return;
|
|
59
|
+
return true;
|
|
37
60
|
}
|
|
38
61
|
|
|
39
62
|
const tableData = response.data.map(device => [
|
|
@@ -58,6 +81,7 @@ export async function listDevices(ctx: CommandContext): Promise<void> {
|
|
|
58
81
|
printSeparator();
|
|
59
82
|
success(`Found ${response.data.length} device(s)`);
|
|
60
83
|
printSeparator();
|
|
84
|
+
return true;
|
|
61
85
|
} else {
|
|
62
86
|
spinner.fail("Failed to fetch devices");
|
|
63
87
|
if (
|
|
@@ -69,10 +93,53 @@ export async function listDevices(ctx: CommandContext): Promise<void> {
|
|
|
69
93
|
} else {
|
|
70
94
|
error(response.error || "Unknown error");
|
|
71
95
|
}
|
|
96
|
+
return false;
|
|
72
97
|
}
|
|
73
98
|
} catch (err) {
|
|
74
99
|
spinner.fail("Error fetching devices");
|
|
75
100
|
error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
-
|
|
101
|
+
return false;
|
|
77
102
|
}
|
|
78
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Loop mode: continuously refresh devices
|
|
107
|
+
*/
|
|
108
|
+
async function loopDevices(ctx: CommandContext): Promise<void> {
|
|
109
|
+
const REFRESH_INTERVAL = 5000; // 5 seconds
|
|
110
|
+
|
|
111
|
+
console.clear();
|
|
112
|
+
printHeader();
|
|
113
|
+
console.log(chalk.cyan("Loop mode enabled. Press Ctrl+C to exit."));
|
|
114
|
+
console.log(
|
|
115
|
+
chalk.gray(`Refreshing every ${REFRESH_INTERVAL / 1000} seconds...\n`),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Handle Ctrl+C gracefully
|
|
119
|
+
process.on("SIGINT", () => {
|
|
120
|
+
console.log("\n");
|
|
121
|
+
info("Loop stopped by user");
|
|
122
|
+
process.exit(0);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Initial fetch
|
|
126
|
+
await fetchAndDisplayDevices(ctx);
|
|
127
|
+
|
|
128
|
+
// Set up refresh loop
|
|
129
|
+
const interval = setInterval(async () => {
|
|
130
|
+
console.clear();
|
|
131
|
+
printHeader();
|
|
132
|
+
console.log(chalk.cyan("Loop mode enabled. Press Ctrl+C to exit."));
|
|
133
|
+
console.log(
|
|
134
|
+
chalk.gray(`Last refresh: ${new Date().toLocaleTimeString()}\n`),
|
|
135
|
+
);
|
|
136
|
+
await fetchAndDisplayDevices(ctx);
|
|
137
|
+
}, REFRESH_INTERVAL);
|
|
138
|
+
|
|
139
|
+
// Keep the process running
|
|
140
|
+
await new Promise(() => {
|
|
141
|
+
// This promise never resolves - we wait for SIGINT
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
clearInterval(interval);
|
|
145
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { debugLog, parseArgs, type CommandContext } from "../utils/context.js";
|
|
3
3
|
import { printHeader } from "../utils/ui.js";
|
|
4
|
+
import { checkForUpdates } from "../utils/update-checker.js";
|
|
4
5
|
|
|
5
6
|
export async function run() {
|
|
6
7
|
const args = process.argv.slice(2);
|
|
@@ -8,6 +9,9 @@ export async function run() {
|
|
|
8
9
|
|
|
9
10
|
const ctx: CommandContext = { debug: flags.debug };
|
|
10
11
|
|
|
12
|
+
// Check for updates (non-blocking)
|
|
13
|
+
checkForUpdates();
|
|
14
|
+
|
|
11
15
|
// Debug mode: log parsed arguments
|
|
12
16
|
debugLog(ctx, "Parsed arguments:", { command, flags, commandArgs });
|
|
13
17
|
|
|
@@ -47,7 +51,7 @@ export async function run() {
|
|
|
47
51
|
case "devices":
|
|
48
52
|
{
|
|
49
53
|
const { listDevices } = await import("./commands/devices.js");
|
|
50
|
-
await listDevices(ctx);
|
|
54
|
+
await listDevices(commandArgs, ctx);
|
|
51
55
|
}
|
|
52
56
|
break;
|
|
53
57
|
case "device":
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if a newer version of the CLI is available on npm
|
|
11
|
+
*/
|
|
12
|
+
export async function checkForUpdates(): Promise<void> {
|
|
13
|
+
try {
|
|
14
|
+
// Get current version from package.json
|
|
15
|
+
const packagePath = join(__dirname, "../../package.json");
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
17
|
+
const currentVersion = packageJson.version;
|
|
18
|
+
const packageName = packageJson.name;
|
|
19
|
+
|
|
20
|
+
// Fetch latest version from npm registry
|
|
21
|
+
const response = await fetch(
|
|
22
|
+
`https://registry.npmjs.org/${packageName}/latest`,
|
|
23
|
+
{
|
|
24
|
+
headers: { Accept: "application/json" },
|
|
25
|
+
signal: AbortSignal.timeout(3000), // 3 second timeout
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
return; // Silently fail if npm check fails
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data = (await response.json()) as { version: string };
|
|
34
|
+
const latestVersion = data.version;
|
|
35
|
+
|
|
36
|
+
// Compare versions
|
|
37
|
+
if (latestVersion && latestVersion !== currentVersion) {
|
|
38
|
+
const isNewer = compareVersions(latestVersion, currentVersion) > 0;
|
|
39
|
+
|
|
40
|
+
if (isNewer) {
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log(
|
|
43
|
+
chalk.yellow.bold("╭─────────────────────────────────────────╮"),
|
|
44
|
+
);
|
|
45
|
+
console.log(
|
|
46
|
+
chalk.yellow.bold("│") +
|
|
47
|
+
chalk.yellow(" Update available! ") +
|
|
48
|
+
chalk.gray(`${currentVersion}`) +
|
|
49
|
+
chalk.yellow(" → ") +
|
|
50
|
+
chalk.green.bold(`${latestVersion}`) +
|
|
51
|
+
chalk.yellow.bold(" │"),
|
|
52
|
+
);
|
|
53
|
+
console.log(
|
|
54
|
+
chalk.yellow.bold("│") +
|
|
55
|
+
chalk.cyan(` Run: npm i -g ${packageName}`) +
|
|
56
|
+
" ".repeat(7) +
|
|
57
|
+
chalk.yellow.bold("│"),
|
|
58
|
+
);
|
|
59
|
+
console.log(
|
|
60
|
+
chalk.yellow.bold("╰─────────────────────────────────────────╯"),
|
|
61
|
+
);
|
|
62
|
+
console.log("");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
// Silently fail - update check is not critical
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Compare two semantic versions
|
|
72
|
+
* Returns: positive if a > b, negative if a < b, 0 if equal
|
|
73
|
+
*/
|
|
74
|
+
function compareVersions(a: string, b: string): number {
|
|
75
|
+
const partsA = a.split(".").map(Number);
|
|
76
|
+
const partsB = b.split(".").map(Number);
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < 3; i++) {
|
|
79
|
+
const partA = partsA[i] || 0;
|
|
80
|
+
const partB = partsB[i] || 0;
|
|
81
|
+
if (partA !== partB) {
|
|
82
|
+
return partA - partB;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return 0;
|
|
86
|
+
}
|