@nordbyte/nordrelay-auto-updater 0.1.0

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.
@@ -0,0 +1,74 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+
5
+ export function commandExists(command) {
6
+ if (!command || /[\\/]/.test(command)) {
7
+ return false;
8
+ }
9
+ const pathEntries = String(process.env.PATH || "").split(path.delimiter).filter(Boolean);
10
+ const extensions = process.platform === "win32"
11
+ ? String(process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean)
12
+ : [""];
13
+ for (const entry of pathEntries) {
14
+ for (const ext of extensions) {
15
+ try {
16
+ accessSync(path.join(entry, `${command}${ext}`), constants.X_OK);
17
+ return true;
18
+ } catch {
19
+ // Continue searching PATH.
20
+ }
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+
26
+ export function runCommand(command, args = [], options = {}) {
27
+ const startedAt = Date.now();
28
+ if (!commandExists(command)) {
29
+ return {
30
+ ok: false,
31
+ command,
32
+ args,
33
+ status: null,
34
+ stdout: "",
35
+ stderr: `${command} not found`,
36
+ durationMs: 0,
37
+ missing: true,
38
+ };
39
+ }
40
+ const result = spawnSync(command, args, {
41
+ encoding: "utf8",
42
+ timeout: Math.max(1000, Number(options.timeoutMs) || 20_000),
43
+ maxBuffer: Math.max(1024 * 1024, Number(options.maxBuffer) || 8 * 1024 * 1024),
44
+ env: buildEnv(options.env),
45
+ });
46
+ const durationMs = Date.now() - startedAt;
47
+ const stdout = String(result.stdout || "");
48
+ const stderr = String(result.stderr || result.error?.message || "");
49
+ const status = typeof result.status === "number" ? result.status : null;
50
+ return {
51
+ ok: !result.error && status === 0,
52
+ command,
53
+ args,
54
+ status,
55
+ stdout,
56
+ stderr,
57
+ durationMs,
58
+ timedOut: Boolean(result.error && /timed out|ETIMEDOUT/i.test(result.error.message)),
59
+ error: result.error?.message,
60
+ };
61
+ }
62
+
63
+ function buildEnv(extra = {}) {
64
+ return {
65
+ PATH: process.env.PATH || "",
66
+ HOME: process.env.HOME || "",
67
+ USERPROFILE: process.env.USERPROFILE || "",
68
+ SystemRoot: process.env.SystemRoot || "",
69
+ npm_config_color: "false",
70
+ NO_COLOR: "1",
71
+ ...extra,
72
+ };
73
+ }
74
+
@@ -0,0 +1,108 @@
1
+ import { runCommand } from "./subprocess.js";
2
+
3
+ const OS_PACKAGE_PATTERN = /^[A-Za-z0-9._+:@-]+$/;
4
+ const NPM_PACKAGE_PATTERN = /^(?:@[A-Za-z0-9._-]+\/)?[A-Za-z0-9._-]+$/;
5
+
6
+ const OS_UPDATE_COMMANDS = {
7
+ apt: (packages) => ["apt-get", packages.length ? ["install", "-y", "--only-upgrade", ...packages] : ["upgrade", "-y"]],
8
+ dnf: (packages) => ["dnf", ["upgrade", "-y", ...packages]],
9
+ pacman: (packages) => ["pacman", packages.length ? ["-S", "--noconfirm", ...packages] : ["-Syu", "--noconfirm"]],
10
+ zypper: (packages) => ["zypper", ["--non-interactive", "update", ...packages]],
11
+ snap: (packages) => ["snap", ["refresh", ...packages]],
12
+ flatpak: (packages) => ["flatpak", ["update", "-y", ...packages]],
13
+ softwareupdate: (packages) => ["softwareupdate", packages.length ? ["-i", ...packages] : ["-i", "-a"]],
14
+ brew: (packages) => ["brew", ["upgrade", ...packages]],
15
+ };
16
+
17
+ export function runOsUpdateAction(input = {}, settings = {}) {
18
+ const grouped = groupOsPackages(input.packages || input.updates || []);
19
+ const managers = Object.keys(grouped);
20
+ if (!managers.length && input.all !== true) {
21
+ return actionResult("update-os", false, [], "Select at least one OS update.");
22
+ }
23
+ const targets = managers.length ? managers : enabledUpdateManagers(input.managers || []);
24
+ const results = targets.map((manager) => {
25
+ const builder = OS_UPDATE_COMMANDS[manager];
26
+ if (!builder) return commandResult(manager, "", [], { ok: false, stderr: `Unsupported package manager: ${manager}`, status: null, durationMs: 0 });
27
+ const [command, args] = builder(grouped[manager] || []);
28
+ return commandResult(manager, command, args, runCommand(command, args, { timeoutMs: settings.updateTimeoutMs || settings.commandTimeoutMs }));
29
+ });
30
+ return actionResult("update-os", results.every((result) => result.ok), results);
31
+ }
32
+
33
+ export function runNpmUpdateAction(input = {}, settings = {}) {
34
+ const packages = normalizePackageNames(input.packages || [], NPM_PACKAGE_PATTERN);
35
+ if (!packages.length && input.all !== true) {
36
+ return actionResult("update-npm", false, [], "Select at least one npm package.");
37
+ }
38
+ const registryArgs = settings.npmRegistry ? ["--registry", String(settings.npmRegistry)] : [];
39
+ const args = packages.length
40
+ ? ["install", "-g", ...packages.map((name) => `${name}@latest`), ...registryArgs]
41
+ : ["update", "-g", ...registryArgs];
42
+ const result = runCommand("npm", args, { timeoutMs: settings.updateTimeoutMs || settings.commandTimeoutMs, maxBuffer: 16 * 1024 * 1024 });
43
+ return actionResult("update-npm", result.ok, [commandResult("npm", "npm", args, result)]);
44
+ }
45
+
46
+ export function runNpmUninstallAction(input = {}, settings = {}) {
47
+ const packages = normalizePackageNames(input.packages || [], NPM_PACKAGE_PATTERN);
48
+ if (!packages.length) {
49
+ return actionResult("uninstall-npm", false, [], "Select at least one npm package.");
50
+ }
51
+ const result = runCommand("npm", ["uninstall", "-g", ...packages], { timeoutMs: settings.updateTimeoutMs || settings.commandTimeoutMs, maxBuffer: 16 * 1024 * 1024 });
52
+ return actionResult("uninstall-npm", result.ok, [commandResult("npm", "npm", ["uninstall", "-g", ...packages], result)]);
53
+ }
54
+
55
+ function groupOsPackages(items) {
56
+ const grouped = {};
57
+ for (const item of Array.isArray(items) ? items : []) {
58
+ const manager = String(item?.manager || "").trim();
59
+ const name = String(item?.name || "").trim();
60
+ if (!manager || !OS_UPDATE_COMMANDS[manager]) continue;
61
+ if (!OS_PACKAGE_PATTERN.test(name)) continue;
62
+ grouped[manager] = grouped[manager] || [];
63
+ if (!grouped[manager].includes(name)) grouped[manager].push(name);
64
+ }
65
+ return grouped;
66
+ }
67
+
68
+ function enabledUpdateManagers(managers) {
69
+ return (Array.isArray(managers) ? managers : [])
70
+ .map((manager) => String(manager || "").trim())
71
+ .filter((manager, index, list) => OS_UPDATE_COMMANDS[manager] && list.indexOf(manager) === index);
72
+ }
73
+
74
+ function normalizePackageNames(packages, pattern) {
75
+ return (Array.isArray(packages) ? packages : [])
76
+ .map((item) => String(typeof item === "string" ? item : item?.name || "").trim())
77
+ .filter((name, index, list) => pattern.test(name) && list.indexOf(name) === index);
78
+ }
79
+
80
+ function commandResult(manager, command, args, result) {
81
+ return {
82
+ manager,
83
+ command,
84
+ args,
85
+ ok: Boolean(result.ok),
86
+ status: result.status,
87
+ durationMs: result.durationMs || 0,
88
+ stdout: tail(result.stdout),
89
+ stderr: tail(result.stderr || result.error),
90
+ missing: Boolean(result.missing),
91
+ timedOut: Boolean(result.timedOut),
92
+ };
93
+ }
94
+
95
+ function actionResult(action, ok, results, error = "") {
96
+ return {
97
+ action,
98
+ ok,
99
+ error,
100
+ results,
101
+ completedAt: new Date().toISOString(),
102
+ };
103
+ }
104
+
105
+ function tail(value, max = 4000) {
106
+ const text = String(value || "").trim();
107
+ return text.length > max ? text.slice(text.length - max) : text;
108
+ }