connector-agent 1.0.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,139 @@
1
+ /**
2
+ * Input Handler — Execute mouse & keyboard commands via robotjs
3
+ */
4
+
5
+ let robot;
6
+
7
+ function initRobot() {
8
+ if (!robot) {
9
+ robot = require('robotjs');
10
+ robot.setMouseDelay(2);
11
+ robot.setKeyboardDelay(10);
12
+ }
13
+ return robot;
14
+ }
15
+
16
+ /**
17
+ * Move mouse to absolute coordinates
18
+ */
19
+ function moveMouse(x, y) {
20
+ const r = initRobot();
21
+ r.moveMouse(x, y);
22
+ return { success: true, action: 'moveMouse', x, y };
23
+ }
24
+
25
+ /**
26
+ * Move mouse by relative delta
27
+ */
28
+ function moveMouseRelative(dx, dy) {
29
+ const r = initRobot();
30
+ const pos = r.getMousePos();
31
+ r.moveMouse(pos.x + dx, pos.y + dy);
32
+ return { success: true, action: 'moveMouseRelative', dx, dy };
33
+ }
34
+
35
+ /**
36
+ * Click mouse button
37
+ */
38
+ function clickMouse(button = 'left') {
39
+ const r = initRobot();
40
+ r.mouseClick(button);
41
+ return { success: true, action: 'clickMouse', button };
42
+ }
43
+
44
+ /**
45
+ * Double click
46
+ */
47
+ function doubleClick() {
48
+ const r = initRobot();
49
+ r.mouseClick('left', true);
50
+ return { success: true, action: 'doubleClick' };
51
+ }
52
+
53
+ /**
54
+ * Scroll mouse
55
+ */
56
+ function scrollMouse(direction = 'down', amount = 3) {
57
+ const r = initRobot();
58
+ const scrollAmount = direction === 'up' ? amount : -amount;
59
+ r.scrollMouse(0, scrollAmount);
60
+ return { success: true, action: 'scrollMouse', direction, amount };
61
+ }
62
+
63
+ /**
64
+ * Type text string
65
+ */
66
+ function typeText(text) {
67
+ const r = initRobot();
68
+ r.typeString(text);
69
+ return { success: true, action: 'typeText', length: text.length };
70
+ }
71
+
72
+ // Key name mapping from nut.js names to robotjs names
73
+ const KEY_MAP = {
74
+ 'Return': 'enter',
75
+ 'Tab': 'tab',
76
+ 'Escape': 'escape',
77
+ 'Space': 'space',
78
+ 'Backspace': 'backspace',
79
+ 'Delete': 'delete',
80
+ 'Up': 'up',
81
+ 'Down': 'down',
82
+ 'Left': 'left',
83
+ 'Right': 'right',
84
+ 'Home': 'home',
85
+ 'End': 'end',
86
+ 'PageUp': 'pageup',
87
+ 'PageDown': 'pagedown',
88
+ 'F1': 'f1', 'F2': 'f2', 'F3': 'f3', 'F4': 'f4',
89
+ 'F5': 'f5', 'F6': 'f6', 'F7': 'f7', 'F8': 'f8',
90
+ 'F9': 'f9', 'F10': 'f10', 'F11': 'f11', 'F12': 'f12',
91
+ 'LeftControl': 'control',
92
+ 'RightControl': 'control',
93
+ 'LeftShift': 'shift',
94
+ 'RightShift': 'shift',
95
+ 'LeftAlt': 'alt',
96
+ 'RightAlt': 'alt',
97
+ 'LeftSuper': 'command',
98
+ 'Enter': 'enter',
99
+ };
100
+
101
+ function mapKey(key) {
102
+ return KEY_MAP[key] || key.toLowerCase();
103
+ }
104
+
105
+ /**
106
+ * Press a single key
107
+ */
108
+ function pressKey(key) {
109
+ const r = initRobot();
110
+ const mapped = mapKey(key);
111
+ r.keyTap(mapped);
112
+ return { success: true, action: 'pressKey', key };
113
+ }
114
+
115
+ /**
116
+ * Press key combination (e.g., Ctrl+C)
117
+ */
118
+ function pressKeyCombo(keys) {
119
+ const r = initRobot();
120
+ if (keys.length < 2) {
121
+ return pressKey(keys[0]);
122
+ }
123
+ // Last key is the main key, rest are modifiers
124
+ const mainKey = mapKey(keys[keys.length - 1]);
125
+ const modifiers = keys.slice(0, -1).map(mapKey);
126
+ r.keyTap(mainKey, modifiers);
127
+ return { success: true, action: 'pressKeyCombo', keys };
128
+ }
129
+
130
+ module.exports = {
131
+ moveMouse,
132
+ moveMouseRelative,
133
+ clickMouse,
134
+ doubleClick,
135
+ scrollMouse,
136
+ typeText,
137
+ pressKey,
138
+ pressKeyCombo,
139
+ };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Screen Capture — Periodic screenshot capture and streaming
3
+ * Uses screenshot-desktop for capture, sharp for JPEG compression
4
+ */
5
+
6
+ const screenshot = require('screenshot-desktop');
7
+ const sharp = require('sharp');
8
+
9
+ class ScreenCapture {
10
+ constructor(socket) {
11
+ this.socket = socket;
12
+ this.streaming = false;
13
+ this.interval = null;
14
+ this.fps = 4; // frames per second
15
+ this.quality = 40; // JPEG quality (lower = smaller = faster)
16
+ this.maxWidth = 1280; // resize for bandwidth
17
+ this.capturing = false; // prevent overlapping captures
18
+ }
19
+
20
+ /**
21
+ * Start streaming screenshots
22
+ */
23
+ start(options = {}) {
24
+ if (this.streaming) return;
25
+
26
+ this.fps = options.fps || this.fps;
27
+ this.quality = options.quality || this.quality;
28
+ this.maxWidth = options.maxWidth || this.maxWidth;
29
+ this.streaming = true;
30
+
31
+ const intervalMs = Math.floor(1000 / this.fps);
32
+
33
+ console.log(` 📸 Screen streaming started (${this.fps} FPS, quality: ${this.quality})`);
34
+
35
+ this.interval = setInterval(() => this.captureAndSend(), intervalMs);
36
+ // Capture immediately
37
+ this.captureAndSend();
38
+ }
39
+
40
+ /**
41
+ * Stop streaming
42
+ */
43
+ stop() {
44
+ if (this.interval) {
45
+ clearInterval(this.interval);
46
+ this.interval = null;
47
+ }
48
+ this.streaming = false;
49
+ console.log(' 📸 Screen streaming stopped');
50
+ }
51
+
52
+ /**
53
+ * Capture one screenshot, compress, and send
54
+ */
55
+ async captureAndSend() {
56
+ if (this.capturing) return; // skip if previous capture still processing
57
+ this.capturing = true;
58
+
59
+ try {
60
+ // Capture raw screenshot as PNG buffer
61
+ const imgBuffer = await screenshot({ format: 'png' });
62
+
63
+ // Get original dimensions
64
+ const metadata = await sharp(imgBuffer).metadata();
65
+
66
+ // Compress to JPEG + resize for bandwidth
67
+ const jpegBuffer = await sharp(imgBuffer)
68
+ .resize({ width: this.maxWidth, withoutEnlargement: true })
69
+ .jpeg({ quality: this.quality, mozjpeg: true })
70
+ .toBuffer();
71
+
72
+ // Send as base64
73
+ const base64 = jpegBuffer.toString('base64');
74
+
75
+ this.socket.emit('screen:frame', {
76
+ data: base64,
77
+ width: metadata.width,
78
+ height: metadata.height,
79
+ displayWidth: Math.min(metadata.width, this.maxWidth),
80
+ timestamp: Date.now(),
81
+ });
82
+ } catch (err) {
83
+ // Silently skip failed frames
84
+ if (!err.message.includes('EPERM')) {
85
+ console.log(` ⚠️ Screen capture error: ${err.message}`);
86
+ }
87
+ } finally {
88
+ this.capturing = false;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Update streaming settings
94
+ */
95
+ updateSettings(options) {
96
+ const wasStreaming = this.streaming;
97
+ if (wasStreaming) this.stop();
98
+ if (options.fps) this.fps = options.fps;
99
+ if (options.quality) this.quality = options.quality;
100
+ if (options.maxWidth) this.maxWidth = options.maxWidth;
101
+ if (wasStreaming) this.start();
102
+ }
103
+ }
104
+
105
+ module.exports = ScreenCapture;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Sleep Preventer — Keeps the system awake
3
+ * Windows: PowerShell cursor refresh (no native deps needed)
4
+ * Mac: caffeinate command (built-in)
5
+ * Linux: xdg-screensaver / systemd-inhibit
6
+ */
7
+
8
+ const { spawn } = require('child_process');
9
+ const os = require('os');
10
+
11
+ class SleepPreventer {
12
+ constructor() {
13
+ this.active = false;
14
+ this.process = null;
15
+ this.platform = os.platform();
16
+ }
17
+
18
+ /**
19
+ * Start preventing sleep — auto-detects OS
20
+ */
21
+ start() {
22
+ if (this.active) return;
23
+ this.active = true;
24
+
25
+ try {
26
+ if (this.platform === 'win32') {
27
+ this._startWindows();
28
+ } else if (this.platform === 'darwin') {
29
+ this._startMac();
30
+ } else {
31
+ this._startLinux();
32
+ }
33
+ console.log(` 💤 Sleep prevention active (${this.platform})`);
34
+ } catch (err) {
35
+ console.log(` ⚠️ Sleep prevention failed: ${err.message}`);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Windows — PowerShell keeps system awake by refreshing cursor position
41
+ * No native dependencies needed, works silently
42
+ */
43
+ _startWindows() {
44
+ const psScript = `
45
+ Add-Type -AssemblyName System.Windows.Forms
46
+ while($true) {
47
+ [System.Windows.Forms.Cursor]::Position = [System.Windows.Forms.Cursor]::Position
48
+ Start-Sleep -Seconds 30
49
+ }
50
+ `.replace(/\n/g, ' ');
51
+
52
+ this.process = spawn('powershell', [
53
+ '-WindowStyle', 'Hidden',
54
+ '-NoProfile',
55
+ '-Command', psScript
56
+ ], {
57
+ windowsHide: true,
58
+ stdio: 'ignore',
59
+ detached: false,
60
+ });
61
+
62
+ this.process.unref();
63
+ this.process.on('error', (e) => console.log(` ⚠️ Sleep preventer error: ${e.message}`));
64
+ }
65
+
66
+ /**
67
+ * Mac — caffeinate is a built-in macOS utility
68
+ * -d: prevent display sleep
69
+ * -i: prevent idle sleep
70
+ * -m: prevent disk sleep
71
+ * -s: prevent system sleep (on AC power)
72
+ * -u: declare user activity (reset idle timer)
73
+ */
74
+ _startMac() {
75
+ this.process = spawn('caffeinate', ['-dimsu'], {
76
+ stdio: 'ignore',
77
+ detached: false,
78
+ });
79
+
80
+ this.process.unref();
81
+ this.process.on('error', (e) => console.log(` ⚠️ Sleep preventer error: ${e.message}`));
82
+ }
83
+
84
+ /**
85
+ * Linux — systemd-inhibit or xdg-screensaver
86
+ */
87
+ _startLinux() {
88
+ // Try systemd-inhibit first (modern Linux)
89
+ this.process = spawn('systemd-inhibit', [
90
+ '--what=idle:sleep',
91
+ '--who=ConnectorAgent',
92
+ '--why=Remote monitoring active',
93
+ 'sleep', 'infinity'
94
+ ], {
95
+ stdio: 'ignore',
96
+ detached: false,
97
+ });
98
+
99
+ this.process.on('error', () => {
100
+ // Fallback: xdg-screensaver
101
+ try {
102
+ this.process = spawn('xdg-screensaver', ['suspend', process.pid.toString()], {
103
+ stdio: 'ignore',
104
+ detached: false,
105
+ });
106
+ } catch (e) {
107
+ console.log(' ⚠️ No sleep prevention available on this Linux');
108
+ }
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Stop preventing sleep — allow system to sleep again
114
+ */
115
+ stop() {
116
+ if (!this.active) return;
117
+ this.active = false;
118
+
119
+ if (this.process) {
120
+ try {
121
+ this.process.kill();
122
+ } catch (e) { /* already dead */ }
123
+ this.process = null;
124
+ }
125
+
126
+ console.log(' 💤 Sleep prevention stopped');
127
+ }
128
+ }
129
+
130
+ module.exports = SleepPreventer;
@@ -0,0 +1,44 @@
1
+ const os = require('os');
2
+
3
+ /**
4
+ * Collect system information for registration
5
+ */
6
+ function getSystemInfo() {
7
+ const networkInterfaces = os.networkInterfaces();
8
+ let ip = '127.0.0.1';
9
+
10
+ // Get first non-internal IPv4 address
11
+ for (const name of Object.keys(networkInterfaces)) {
12
+ for (const iface of networkInterfaces[name]) {
13
+ if (iface.family === 'IPv4' && !iface.internal) {
14
+ ip = iface.address;
15
+ break;
16
+ }
17
+ }
18
+ }
19
+
20
+ return {
21
+ hostname: os.hostname(),
22
+ username: os.userInfo().username,
23
+ os: `${os.type()} ${os.release()} (${os.arch()})`,
24
+ ip,
25
+ platform: os.platform(),
26
+ cpus: os.cpus().length,
27
+ totalMemory: Math.round(os.totalmem() / (1024 * 1024 * 1024)) + ' GB',
28
+ uptime: Math.round(os.uptime() / 3600) + ' hours',
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Get running processes (basic — using os module)
34
+ */
35
+ function getBasicSystemStats() {
36
+ return {
37
+ freeMemory: Math.round(os.freemem() / (1024 * 1024)) + ' MB',
38
+ totalMemory: Math.round(os.totalmem() / (1024 * 1024)) + ' MB',
39
+ uptime: Math.round(os.uptime()) + ' seconds',
40
+ loadAvg: os.loadavg(),
41
+ };
42
+ }
43
+
44
+ module.exports = { getSystemInfo, getBasicSystemStats };