@fredlackey/devutils 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.
Files changed (200) hide show
  1. package/README.md +156 -0
  2. package/bin/dev.js +16 -0
  3. package/files/README.md +0 -0
  4. package/files/claude/.claude/commands/setup-context.md +3 -0
  5. package/files/monorepos/_archive/README.md +36 -0
  6. package/files/monorepos/_legacy/README.md +36 -0
  7. package/files/monorepos/ai-docs/README.md +33 -0
  8. package/files/monorepos/apps/README.md +24 -0
  9. package/files/monorepos/docs/README.md +40 -0
  10. package/files/monorepos/packages/README.md +25 -0
  11. package/files/monorepos/research/README.md +29 -0
  12. package/files/monorepos/scripts/README.md +24 -0
  13. package/package.json +39 -0
  14. package/src/cli.js +68 -0
  15. package/src/commands/README.md +41 -0
  16. package/src/commands/configure.js +199 -0
  17. package/src/commands/identity.js +1630 -0
  18. package/src/commands/ignore.js +247 -0
  19. package/src/commands/install.js +173 -0
  20. package/src/commands/setup.js +212 -0
  21. package/src/commands/status.js +223 -0
  22. package/src/completion.js +284 -0
  23. package/src/constants.js +45 -0
  24. package/src/ignore/claude-code.txt +10 -0
  25. package/src/ignore/docker.txt +18 -0
  26. package/src/ignore/linux.txt +23 -0
  27. package/src/ignore/macos.txt +36 -0
  28. package/src/ignore/node.txt +55 -0
  29. package/src/ignore/terraform.txt +37 -0
  30. package/src/ignore/vscode.txt +18 -0
  31. package/src/ignore/windows.txt +35 -0
  32. package/src/index.js +0 -0
  33. package/src/installs/README.md +399 -0
  34. package/src/installs/adobe-creative-cloud.js +44 -0
  35. package/src/installs/appcleaner.js +44 -0
  36. package/src/installs/atomicparsley.js +44 -0
  37. package/src/installs/aws-cli.js +44 -0
  38. package/src/installs/balena-etcher.js +44 -0
  39. package/src/installs/bambu-studio.js +44 -0
  40. package/src/installs/bash-completion.js +44 -0
  41. package/src/installs/bash.js +44 -0
  42. package/src/installs/beyond-compare.js +44 -0
  43. package/src/installs/build-essential.js +44 -0
  44. package/src/installs/caffeine.js +44 -0
  45. package/src/installs/camtasia.js +44 -0
  46. package/src/installs/chatgpt.js +44 -0
  47. package/src/installs/chrome-canary.js +44 -0
  48. package/src/installs/chromium.js +44 -0
  49. package/src/installs/claude-code.js +44 -0
  50. package/src/installs/curl.js +44 -0
  51. package/src/installs/cursor.js +44 -0
  52. package/src/installs/dbschema.js +44 -0
  53. package/src/installs/docker.js +44 -0
  54. package/src/installs/drawio.js +44 -0
  55. package/src/installs/elmedia-player.js +44 -0
  56. package/src/installs/ffmpeg.js +44 -0
  57. package/src/installs/gemini-cli.js +44 -0
  58. package/src/installs/git.js +44 -0
  59. package/src/installs/gitego.js +44 -0
  60. package/src/installs/go.js +44 -0
  61. package/src/installs/google-chrome.js +44 -0
  62. package/src/installs/gpg.js +141 -0
  63. package/src/installs/homebrew.js +44 -0
  64. package/src/installs/imageoptim.js +44 -0
  65. package/src/installs/jq.js +44 -0
  66. package/src/installs/keyboard-maestro.js +44 -0
  67. package/src/installs/latex.js +44 -0
  68. package/src/installs/lftp.js +44 -0
  69. package/src/installs/messenger.js +44 -0
  70. package/src/installs/microsoft-office.js +44 -0
  71. package/src/installs/microsoft-teams.js +44 -0
  72. package/src/installs/node.js +44 -0
  73. package/src/installs/nordpass.js +44 -0
  74. package/src/installs/nvm.js +44 -0
  75. package/src/installs/openssh.js +134 -0
  76. package/src/installs/pandoc.js +44 -0
  77. package/src/installs/pinentry.js +44 -0
  78. package/src/installs/pngyu.js +44 -0
  79. package/src/installs/postman.js +44 -0
  80. package/src/installs/safari-tech-preview.js +44 -0
  81. package/src/installs/sfnt2woff.js +44 -0
  82. package/src/installs/shellcheck.js +44 -0
  83. package/src/installs/slack.js +44 -0
  84. package/src/installs/snagit.js +44 -0
  85. package/src/installs/spotify.js +44 -0
  86. package/src/installs/studio-3t.js +44 -0
  87. package/src/installs/sublime-text.js +44 -0
  88. package/src/installs/superwhisper.js +44 -0
  89. package/src/installs/tailscale.js +44 -0
  90. package/src/installs/termius.js +44 -0
  91. package/src/installs/terraform.js +44 -0
  92. package/src/installs/tidal.js +44 -0
  93. package/src/installs/tmux.js +44 -0
  94. package/src/installs/tree.js +44 -0
  95. package/src/installs/vim.js +44 -0
  96. package/src/installs/vlc.js +44 -0
  97. package/src/installs/vscode.js +44 -0
  98. package/src/installs/whatsapp.js +44 -0
  99. package/src/installs/woff2.js +44 -0
  100. package/src/installs/xcode.js +44 -0
  101. package/src/installs/yarn.js +44 -0
  102. package/src/installs/yq.js +44 -0
  103. package/src/installs/yt-dlp.js +44 -0
  104. package/src/installs/zoom.js +44 -0
  105. package/src/scripts/README.md +95 -0
  106. package/src/scripts/afk.js +23 -0
  107. package/src/scripts/backup-all.js +24 -0
  108. package/src/scripts/backup-source.js +24 -0
  109. package/src/scripts/brewd.js +23 -0
  110. package/src/scripts/brewi.js +24 -0
  111. package/src/scripts/brewr.js +24 -0
  112. package/src/scripts/brews.js +24 -0
  113. package/src/scripts/brewu.js +23 -0
  114. package/src/scripts/c.js +23 -0
  115. package/src/scripts/ccurl.js +24 -0
  116. package/src/scripts/certbot-crontab-init.js +24 -0
  117. package/src/scripts/certbot-init.js +25 -0
  118. package/src/scripts/ch.js +23 -0
  119. package/src/scripts/claude-danger.js +23 -0
  120. package/src/scripts/clean-dev.js +24 -0
  121. package/src/scripts/clear-dns-cache.js +23 -0
  122. package/src/scripts/clone.js +25 -0
  123. package/src/scripts/code-all.js +24 -0
  124. package/src/scripts/count-files.js +24 -0
  125. package/src/scripts/count-folders.js +24 -0
  126. package/src/scripts/count.js +24 -0
  127. package/src/scripts/d.js +23 -0
  128. package/src/scripts/datauri.js +24 -0
  129. package/src/scripts/delete-files.js +24 -0
  130. package/src/scripts/docker-clean.js +24 -0
  131. package/src/scripts/dp.js +23 -0
  132. package/src/scripts/e.js +24 -0
  133. package/src/scripts/empty-trash.js +23 -0
  134. package/src/scripts/evm.js +25 -0
  135. package/src/scripts/fetch-github-repos.js +25 -0
  136. package/src/scripts/get-channel.js +24 -0
  137. package/src/scripts/get-course.js +26 -0
  138. package/src/scripts/get-dependencies.js +25 -0
  139. package/src/scripts/get-folder.js +26 -0
  140. package/src/scripts/get-tunes.js +25 -0
  141. package/src/scripts/get-video.js +24 -0
  142. package/src/scripts/git-backup.js +25 -0
  143. package/src/scripts/git-clone.js +25 -0
  144. package/src/scripts/git-pup.js +23 -0
  145. package/src/scripts/git-push.js +24 -0
  146. package/src/scripts/h.js +24 -0
  147. package/src/scripts/hide-desktop-icons.js +23 -0
  148. package/src/scripts/hide-hidden-files.js +23 -0
  149. package/src/scripts/install-dependencies-from.js +25 -0
  150. package/src/scripts/ips.js +26 -0
  151. package/src/scripts/iso.js +24 -0
  152. package/src/scripts/killni.js +23 -0
  153. package/src/scripts/ll.js +24 -0
  154. package/src/scripts/local-ip.js +23 -0
  155. package/src/scripts/m.js +24 -0
  156. package/src/scripts/map.js +24 -0
  157. package/src/scripts/mkd.js +24 -0
  158. package/src/scripts/ncu-update-all.js +24 -0
  159. package/src/scripts/nginx-init.js +28 -0
  160. package/src/scripts/npmi.js +23 -0
  161. package/src/scripts/o.js +24 -0
  162. package/src/scripts/org-by-date.js +24 -0
  163. package/src/scripts/p.js +23 -0
  164. package/src/scripts/packages.js +25 -0
  165. package/src/scripts/path.js +23 -0
  166. package/src/scripts/ports.js +23 -0
  167. package/src/scripts/q.js +23 -0
  168. package/src/scripts/refresh-files.js +26 -0
  169. package/src/scripts/remove-smaller-files.js +24 -0
  170. package/src/scripts/rename-files-with-date.js +25 -0
  171. package/src/scripts/resize-image.js +25 -0
  172. package/src/scripts/rm-safe.js +24 -0
  173. package/src/scripts/s.js +24 -0
  174. package/src/scripts/set-git-public.js +23 -0
  175. package/src/scripts/show-desktop-icons.js +23 -0
  176. package/src/scripts/show-hidden-files.js +23 -0
  177. package/src/scripts/tpa.js +23 -0
  178. package/src/scripts/tpo.js +23 -0
  179. package/src/scripts/u.js +23 -0
  180. package/src/scripts/vpush.js +23 -0
  181. package/src/scripts/y.js +23 -0
  182. package/src/utils/README.md +95 -0
  183. package/src/utils/common/apps.js +143 -0
  184. package/src/utils/common/display.js +157 -0
  185. package/src/utils/common/network.js +185 -0
  186. package/src/utils/common/os.js +202 -0
  187. package/src/utils/common/package-manager.js +301 -0
  188. package/src/utils/common/privileges.js +138 -0
  189. package/src/utils/common/shell.js +195 -0
  190. package/src/utils/macos/apps.js +228 -0
  191. package/src/utils/macos/brew.js +315 -0
  192. package/src/utils/ubuntu/apt.js +301 -0
  193. package/src/utils/ubuntu/desktop.js +292 -0
  194. package/src/utils/ubuntu/snap.js +302 -0
  195. package/src/utils/ubuntu/systemd.js +286 -0
  196. package/src/utils/windows/choco.js +327 -0
  197. package/src/utils/windows/env.js +246 -0
  198. package/src/utils/windows/registry.js +269 -0
  199. package/src/utils/windows/shell.js +240 -0
  200. package/src/utils/windows/winget.js +378 -0
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Generic Application Detection Utilities
5
+ *
6
+ * Cross-platform utilities for detecting installed applications.
7
+ * Delegates to OS-specific implementations for actual detection.
8
+ */
9
+
10
+ const osUtils = require('./os');
11
+ const shell = require('./shell');
12
+
13
+ /**
14
+ * Cross-platform check if an application is installed
15
+ * Delegates to OS-specific implementations
16
+ * @param {string} appName - The application name to check
17
+ * @returns {Promise<boolean>}
18
+ */
19
+ async function isInstalled(appName) {
20
+ const platform = osUtils.detect();
21
+
22
+ switch (platform.type) {
23
+ case 'macos': {
24
+ const macosApps = require('../macos/apps');
25
+ return macosApps.isAppInstalled(appName);
26
+ }
27
+ case 'ubuntu':
28
+ case 'debian':
29
+ case 'raspbian': {
30
+ const apt = require('../ubuntu/apt');
31
+ const snap = require('../ubuntu/snap');
32
+ // Check both apt and snap
33
+ const aptInstalled = await apt.isPackageInstalled(appName);
34
+ if (aptInstalled) return true;
35
+ const snapInstalled = await snap.isSnapInstalled(appName);
36
+ return snapInstalled;
37
+ }
38
+ case 'windows': {
39
+ const registry = require('../windows/registry');
40
+ return registry.isAppInstalled(appName);
41
+ }
42
+ default:
43
+ // Fallback: check if command exists in PATH
44
+ return shell.commandExists(appName);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Retrieves the installed version of an application
50
+ * @param {string} appName - The application name
51
+ * @returns {Promise<string|null>}
52
+ */
53
+ async function getVersion(appName) {
54
+ const platform = osUtils.detect();
55
+
56
+ switch (platform.type) {
57
+ case 'macos': {
58
+ const macosApps = require('../macos/apps');
59
+ return macosApps.getAppVersion(appName);
60
+ }
61
+ case 'ubuntu':
62
+ case 'debian':
63
+ case 'raspbian': {
64
+ const apt = require('../ubuntu/apt');
65
+ const snap = require('../ubuntu/snap');
66
+ // Check apt first
67
+ const aptVersion = await apt.getPackageVersion(appName);
68
+ if (aptVersion) return aptVersion;
69
+ // Then snap
70
+ const snapVersion = await snap.getSnapVersion(appName);
71
+ return snapVersion;
72
+ }
73
+ case 'windows': {
74
+ const registry = require('../windows/registry');
75
+ return registry.getAppVersion(appName);
76
+ }
77
+ default:
78
+ // Fallback: try running appName --version
79
+ return getVersionFromCommand(appName);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Returns the installation path of an application
85
+ * @param {string} appName - The application name
86
+ * @returns {Promise<string|null>}
87
+ */
88
+ async function getInstallPath(appName) {
89
+ const platform = osUtils.detect();
90
+
91
+ switch (platform.type) {
92
+ case 'macos': {
93
+ const macosApps = require('../macos/apps');
94
+ return macosApps.getAppBundlePath(appName);
95
+ }
96
+ case 'windows': {
97
+ const registry = require('../windows/registry');
98
+ return registry.getInstallPath(appName);
99
+ }
100
+ default:
101
+ // Fallback: try to find the executable
102
+ return shell.which(appName);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Tries to get version by running the command with --version flag
108
+ * @param {string} command - The command to check
109
+ * @returns {Promise<string|null>}
110
+ */
111
+ async function getVersionFromCommand(command) {
112
+ const versionFlags = ['--version', '-v', '-V', 'version'];
113
+
114
+ for (const flag of versionFlags) {
115
+ const result = await shell.exec(`${command} ${flag}`);
116
+ if (result.code === 0 && result.stdout) {
117
+ // Try to extract version number from output
118
+ const versionMatch = result.stdout.match(/(\d+\.\d+\.?\d*)/);
119
+ if (versionMatch) {
120
+ return versionMatch[1];
121
+ }
122
+ }
123
+ }
124
+
125
+ return null;
126
+ }
127
+
128
+ /**
129
+ * Checks if a command-line tool is available
130
+ * @param {string} command - The command to check
131
+ * @returns {boolean}
132
+ */
133
+ function isCommandAvailable(command) {
134
+ return shell.commandExists(command);
135
+ }
136
+
137
+ module.exports = {
138
+ isInstalled,
139
+ getVersion,
140
+ getInstallPath,
141
+ getVersionFromCommand,
142
+ isCommandAvailable
143
+ };
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Display and GUI Detection Utilities
5
+ *
6
+ * Platform-agnostic utilities for detecting display server availability.
7
+ * Used to determine if GUI applications can be installed/run.
8
+ */
9
+
10
+ const fs = require('fs');
11
+
12
+ /**
13
+ * Checks if a display server is available
14
+ * On macOS: Always true (Aqua/Quartz is always present)
15
+ * On Windows: Always true (Windows Desktop is always present)
16
+ * On Linux: Checks for X11 or Wayland display
17
+ * @returns {boolean}
18
+ */
19
+ function hasDisplay() {
20
+ const platform = process.platform;
21
+
22
+ // macOS always has a display (Aqua/Quartz)
23
+ if (platform === 'darwin') {
24
+ return true;
25
+ }
26
+
27
+ // Windows always has a display (unless running as a service, which is rare)
28
+ if (platform === 'win32') {
29
+ return true;
30
+ }
31
+
32
+ // Linux: check for X11 or Wayland
33
+ if (platform === 'linux') {
34
+ return hasX11Display() || hasWaylandDisplay();
35
+ }
36
+
37
+ return false;
38
+ }
39
+
40
+ /**
41
+ * Checks if running in a headless environment (no GUI)
42
+ * @returns {boolean}
43
+ */
44
+ function isHeadless() {
45
+ return !hasDisplay();
46
+ }
47
+
48
+ /**
49
+ * Checks if X11 display is available
50
+ * @returns {boolean}
51
+ */
52
+ function hasX11Display() {
53
+ // Check DISPLAY environment variable
54
+ if (process.env.DISPLAY) {
55
+ return true;
56
+ }
57
+
58
+ // Check if X server socket exists
59
+ const x11SocketPath = '/tmp/.X11-unix';
60
+ if (fs.existsSync(x11SocketPath)) {
61
+ try {
62
+ const sockets = fs.readdirSync(x11SocketPath);
63
+ return sockets.length > 0;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ return false;
70
+ }
71
+
72
+ /**
73
+ * Checks if Wayland display is available
74
+ * @returns {boolean}
75
+ */
76
+ function hasWaylandDisplay() {
77
+ // Check WAYLAND_DISPLAY environment variable
78
+ if (process.env.WAYLAND_DISPLAY) {
79
+ return true;
80
+ }
81
+
82
+ // Check if Wayland socket exists in XDG_RUNTIME_DIR
83
+ const runtimeDir = process.env.XDG_RUNTIME_DIR;
84
+ if (runtimeDir) {
85
+ const waylandSocketPath = `${runtimeDir}/wayland-0`;
86
+ if (fs.existsSync(waylandSocketPath)) {
87
+ return true;
88
+ }
89
+ }
90
+
91
+ return false;
92
+ }
93
+
94
+ /**
95
+ * Gets the type of display server in use
96
+ * @returns {'aqua'|'windows'|'x11'|'wayland'|null}
97
+ */
98
+ function getDisplayType() {
99
+ const platform = process.platform;
100
+
101
+ if (platform === 'darwin') {
102
+ return 'aqua';
103
+ }
104
+
105
+ if (platform === 'win32') {
106
+ return 'windows';
107
+ }
108
+
109
+ if (platform === 'linux') {
110
+ if (hasWaylandDisplay()) {
111
+ return 'wayland';
112
+ }
113
+ if (hasX11Display()) {
114
+ return 'x11';
115
+ }
116
+ }
117
+
118
+ return null;
119
+ }
120
+
121
+ /**
122
+ * Gets the display variable value (DISPLAY or WAYLAND_DISPLAY)
123
+ * @returns {string|null}
124
+ */
125
+ function getDisplayVariable() {
126
+ if (process.env.WAYLAND_DISPLAY) {
127
+ return process.env.WAYLAND_DISPLAY;
128
+ }
129
+ if (process.env.DISPLAY) {
130
+ return process.env.DISPLAY;
131
+ }
132
+ return null;
133
+ }
134
+
135
+ /**
136
+ * Checks if SSH session has X11 forwarding enabled
137
+ * @returns {boolean}
138
+ */
139
+ function hasX11Forwarding() {
140
+ // Check if we're in an SSH session with DISPLAY set
141
+ if (process.env.SSH_CONNECTION && process.env.DISPLAY) {
142
+ // DISPLAY typically looks like "localhost:10.0" for X11 forwarding
143
+ const display = process.env.DISPLAY;
144
+ return display.includes(':');
145
+ }
146
+ return false;
147
+ }
148
+
149
+ module.exports = {
150
+ hasDisplay,
151
+ isHeadless,
152
+ hasX11Display,
153
+ hasWaylandDisplay,
154
+ getDisplayType,
155
+ getDisplayVariable,
156
+ hasX11Forwarding
157
+ };
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Network Connectivity Utilities
5
+ *
6
+ * Platform-agnostic utilities for checking network connectivity.
7
+ */
8
+
9
+ const dns = require('dns');
10
+ const https = require('https');
11
+ const http = require('http');
12
+
13
+ /**
14
+ * Checks if the machine has internet connectivity
15
+ * @param {number} [timeout=5000] - Timeout in milliseconds
16
+ * @returns {Promise<boolean>}
17
+ */
18
+ async function isOnline(timeout = 5000) {
19
+ // Try multiple reliable hosts in case one is blocked
20
+ const hosts = [
21
+ 'dns.google',
22
+ 'cloudflare.com',
23
+ '1.1.1.1'
24
+ ];
25
+
26
+ for (const host of hosts) {
27
+ const reachable = await canReach(host, timeout);
28
+ if (reachable) {
29
+ return true;
30
+ }
31
+ }
32
+
33
+ return false;
34
+ }
35
+
36
+ /**
37
+ * Tests if a specific hostname is reachable
38
+ * @param {string} hostname - The hostname to check
39
+ * @param {number} [timeout=5000] - Timeout in milliseconds
40
+ * @returns {Promise<boolean>}
41
+ */
42
+ async function canReach(hostname, timeout = 5000) {
43
+ return new Promise((resolve) => {
44
+ // First try DNS lookup
45
+ const timer = setTimeout(() => {
46
+ resolve(false);
47
+ }, timeout);
48
+
49
+ dns.lookup(hostname, (err) => {
50
+ clearTimeout(timer);
51
+ if (err) {
52
+ resolve(false);
53
+ } else {
54
+ resolve(true);
55
+ }
56
+ });
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Tests if a URL is reachable via HTTP(S) request
62
+ * @param {string} url - The URL to check
63
+ * @param {number} [timeout=5000] - Timeout in milliseconds
64
+ * @returns {Promise<boolean>}
65
+ */
66
+ async function canFetch(url, timeout = 5000) {
67
+ return new Promise((resolve) => {
68
+ const timer = setTimeout(() => {
69
+ resolve(false);
70
+ }, timeout);
71
+
72
+ const protocol = url.startsWith('https') ? https : http;
73
+
74
+ const req = protocol.get(url, { timeout }, (res) => {
75
+ clearTimeout(timer);
76
+ // Consider 2xx and 3xx as success
77
+ resolve(res.statusCode >= 200 && res.statusCode < 400);
78
+ res.destroy();
79
+ });
80
+
81
+ req.on('error', () => {
82
+ clearTimeout(timer);
83
+ resolve(false);
84
+ });
85
+
86
+ req.on('timeout', () => {
87
+ clearTimeout(timer);
88
+ req.destroy();
89
+ resolve(false);
90
+ });
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Gets the public IP address of the machine
96
+ * @param {number} [timeout=5000] - Timeout in milliseconds
97
+ * @returns {Promise<string|null>}
98
+ */
99
+ async function getPublicIP(timeout = 5000) {
100
+ const services = [
101
+ 'https://api.ipify.org',
102
+ 'https://icanhazip.com',
103
+ 'https://ifconfig.me/ip'
104
+ ];
105
+
106
+ for (const service of services) {
107
+ try {
108
+ const ip = await fetchText(service, timeout);
109
+ if (ip && isValidIP(ip.trim())) {
110
+ return ip.trim();
111
+ }
112
+ } catch {
113
+ continue;
114
+ }
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ /**
121
+ * Fetches text content from a URL
122
+ * @param {string} url - The URL to fetch
123
+ * @param {number} [timeout=5000] - Timeout in milliseconds
124
+ * @returns {Promise<string>}
125
+ */
126
+ async function fetchText(url, timeout = 5000) {
127
+ return new Promise((resolve, reject) => {
128
+ const timer = setTimeout(() => {
129
+ reject(new Error('Timeout'));
130
+ }, timeout);
131
+
132
+ const protocol = url.startsWith('https') ? https : http;
133
+
134
+ const req = protocol.get(url, { timeout }, (res) => {
135
+ let data = '';
136
+ res.on('data', (chunk) => {
137
+ data += chunk;
138
+ });
139
+ res.on('end', () => {
140
+ clearTimeout(timer);
141
+ resolve(data);
142
+ });
143
+ });
144
+
145
+ req.on('error', (err) => {
146
+ clearTimeout(timer);
147
+ reject(err);
148
+ });
149
+
150
+ req.on('timeout', () => {
151
+ clearTimeout(timer);
152
+ req.destroy();
153
+ reject(new Error('Timeout'));
154
+ });
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Validates an IP address format
160
+ * @param {string} ip - The IP to validate
161
+ * @returns {boolean}
162
+ */
163
+ function isValidIP(ip) {
164
+ // IPv4 regex
165
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
166
+ // IPv6 regex (simplified)
167
+ const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
168
+
169
+ if (ipv4Regex.test(ip)) {
170
+ const parts = ip.split('.');
171
+ return parts.every((part) => {
172
+ const num = parseInt(part, 10);
173
+ return num >= 0 && num <= 255;
174
+ });
175
+ }
176
+
177
+ return ipv6Regex.test(ip);
178
+ }
179
+
180
+ module.exports = {
181
+ isOnline,
182
+ canReach,
183
+ canFetch,
184
+ getPublicIP
185
+ };
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Operating System Detection Utilities
5
+ *
6
+ * Platform-agnostic utilities for detecting the current operating system,
7
+ * architecture, and distribution. Uses Node.js APIs that work identically
8
+ * on all platforms.
9
+ */
10
+
11
+ const os = require('os');
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ /**
16
+ * Detects the current operating system and returns platform details
17
+ * @returns {{ type: string, packageManager: string|null, distro: string|null }}
18
+ */
19
+ function detect() {
20
+ const platform = process.platform;
21
+ const result = {
22
+ type: 'unknown',
23
+ packageManager: null,
24
+ distro: null
25
+ };
26
+
27
+ if (platform === 'darwin') {
28
+ result.type = 'macos';
29
+ result.packageManager = 'brew';
30
+ result.distro = 'macos';
31
+ return result;
32
+ }
33
+
34
+ if (platform === 'win32') {
35
+ // Check for WSL
36
+ if (process.env.WSL_DISTRO_NAME) {
37
+ result.type = 'wsl';
38
+ result.packageManager = 'apt';
39
+ result.distro = process.env.WSL_DISTRO_NAME.toLowerCase();
40
+ return result;
41
+ }
42
+ result.type = 'windows';
43
+ result.packageManager = 'winget';
44
+ result.distro = 'windows';
45
+ return result;
46
+ }
47
+
48
+ if (platform === 'linux') {
49
+ // Check for WSL first
50
+ if (process.env.WSL_DISTRO_NAME) {
51
+ result.type = 'wsl';
52
+ result.packageManager = 'apt';
53
+ result.distro = process.env.WSL_DISTRO_NAME.toLowerCase();
54
+ return result;
55
+ }
56
+
57
+ // Try to detect Linux distribution
58
+ const distro = getDistro();
59
+ result.distro = distro;
60
+
61
+ // Debian-based (Ubuntu, Debian, Raspberry Pi OS)
62
+ if (fs.existsSync('/etc/debian_version')) {
63
+ if (distro === 'raspbian' || distro === 'raspberry') {
64
+ result.type = 'raspbian';
65
+ } else if (distro === 'ubuntu') {
66
+ result.type = 'ubuntu';
67
+ } else {
68
+ result.type = 'debian';
69
+ }
70
+ result.packageManager = 'apt';
71
+ return result;
72
+ }
73
+
74
+ // RHEL-based (Amazon Linux, CentOS, Fedora, RHEL)
75
+ if (fs.existsSync('/etc/redhat-release')) {
76
+ if (distro === 'amzn' || distro === 'amazon') {
77
+ result.type = 'amazon_linux';
78
+ } else if (distro === 'fedora') {
79
+ result.type = 'fedora';
80
+ } else {
81
+ result.type = 'rhel';
82
+ }
83
+ // Use dnf if available, otherwise yum
84
+ result.packageManager = fs.existsSync('/usr/bin/dnf') ? 'dnf' : 'yum';
85
+ return result;
86
+ }
87
+
88
+ // Fallback for unknown Linux
89
+ result.type = 'linux';
90
+ return result;
91
+ }
92
+
93
+ return result;
94
+ }
95
+
96
+ /**
97
+ * Checks if running on Windows (native, not WSL)
98
+ * @returns {boolean}
99
+ */
100
+ function isWindows() {
101
+ return process.platform === 'win32' && !process.env.WSL_DISTRO_NAME;
102
+ }
103
+
104
+ /**
105
+ * Checks if running on macOS
106
+ * @returns {boolean}
107
+ */
108
+ function isMacOS() {
109
+ return process.platform === 'darwin';
110
+ }
111
+
112
+ /**
113
+ * Checks if running on any Linux distribution
114
+ * @returns {boolean}
115
+ */
116
+ function isLinux() {
117
+ return process.platform === 'linux';
118
+ }
119
+
120
+ /**
121
+ * Checks if running inside Windows Subsystem for Linux
122
+ * @returns {boolean}
123
+ */
124
+ function isWSL() {
125
+ return !!process.env.WSL_DISTRO_NAME;
126
+ }
127
+
128
+ /**
129
+ * Returns the CPU architecture
130
+ * @returns {string} - 'x64', 'arm64', 'ia32', etc.
131
+ */
132
+ function getArch() {
133
+ return process.arch;
134
+ }
135
+
136
+ /**
137
+ * For Linux, returns the specific distribution name
138
+ * @returns {string|null} - Distribution name (ubuntu, debian, fedora, etc.) or null
139
+ */
140
+ function getDistro() {
141
+ if (process.platform !== 'linux') {
142
+ return null;
143
+ }
144
+
145
+ // Try /etc/os-release first (most modern Linux distros)
146
+ const osReleasePath = '/etc/os-release';
147
+ if (fs.existsSync(osReleasePath)) {
148
+ try {
149
+ const content = fs.readFileSync(osReleasePath, 'utf8');
150
+ const idMatch = content.match(/^ID=["']?([^"'\n]+)["']?/m);
151
+ if (idMatch) {
152
+ return idMatch[1].toLowerCase();
153
+ }
154
+ } catch (err) {
155
+ // Fall through to other methods
156
+ }
157
+ }
158
+
159
+ // Try /etc/lsb-release (Ubuntu and some others)
160
+ const lsbReleasePath = '/etc/lsb-release';
161
+ if (fs.existsSync(lsbReleasePath)) {
162
+ try {
163
+ const content = fs.readFileSync(lsbReleasePath, 'utf8');
164
+ const idMatch = content.match(/^DISTRIB_ID=["']?([^"'\n]+)["']?/m);
165
+ if (idMatch) {
166
+ return idMatch[1].toLowerCase();
167
+ }
168
+ } catch (err) {
169
+ // Fall through
170
+ }
171
+ }
172
+
173
+ return null;
174
+ }
175
+
176
+ /**
177
+ * Returns the home directory path
178
+ * @returns {string}
179
+ */
180
+ function getHomeDir() {
181
+ return os.homedir();
182
+ }
183
+
184
+ /**
185
+ * Returns the temporary directory path
186
+ * @returns {string}
187
+ */
188
+ function getTempDir() {
189
+ return os.tmpdir();
190
+ }
191
+
192
+ module.exports = {
193
+ detect,
194
+ isWindows,
195
+ isMacOS,
196
+ isLinux,
197
+ isWSL,
198
+ getArch,
199
+ getDistro,
200
+ getHomeDir,
201
+ getTempDir
202
+ };