@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 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 readFileSync2, unlinkSync, writeFileSync } from "fs";
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 = readFileSync2(this.tempFile);
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 readFileSync3, writeFileSync as writeFileSync2 } from "fs";
30804
+ import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
30805
30805
  import { homedir } from "os";
30806
- import { join as join2 } from "path";
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 = readFileSync3(CONFIG_FILE, "utf-8");
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 = join2(homedir(), ".syncstuff");
30828
- CONFIG_FILE = join2(CONFIG_DIR, "config.json");
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
- printHeader();
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
- process.exit(1);
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 readFileSync4 } from "fs";
31477
- import { dirname, join as join3 } from "path";
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 = join3(__dirname2, "../../../package.json");
31483
- const packageJson = JSON.parse(readFileSync4(packagePath, "utf-8"));
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 = join3(__dirname2, "../../../package.json");
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 __filename2, __dirname2;
31544
+ var __filename3, __dirname3;
31510
31545
  var init_version = __esm(() => {
31511
31546
  init_source();
31512
31547
  init_core();
31513
31548
  init_ui();
31514
- __filename2 = fileURLToPath(import.meta.url);
31515
- __dirname2 = dirname(__filename2);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@involvex/syncstuff-cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "homepage": "https://syncstuff-web.involvex.workers.dev/",
6
6
  "sponsor": {
@@ -11,8 +11,31 @@ import {
11
11
  success,
12
12
  } from "../../utils/ui.js";
13
13
 
14
- export async function listDevices(ctx: CommandContext): Promise<void> {
15
- printHeader();
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
- process.exit(1);
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
+ }
@@ -43,6 +43,5 @@ export function showversion() {
43
43
  console.log("Packagepath: " + packagePath);
44
44
  console.log(chalk.yellow("Version: 0.0.1 (unable to read package.json)"));
45
45
  }
46
- console.log(chalk.yellow("Version: 0.0.1 (unable to read package.json)"));
47
46
  }
48
47
  }
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
+ }