vortix 1.0.0 → 1.0.2

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/agent/agent.js ADDED
@@ -0,0 +1,270 @@
1
+ const WebSocket = require("ws");
2
+ const { exec } = require("child_process");
3
+ const os = require("os");
4
+ const readline = require("readline");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+
8
+ process.on("uncaughtException", (err) => {
9
+ console.error("Uncaught Exception:", err);
10
+ });
11
+
12
+ process.on("unhandledRejection", (err) => {
13
+ console.error("Unhandled Rejection:", err);
14
+ });
15
+
16
+ const CONFIG_FILE = path.join(os.homedir(), '.vortix-config.json');
17
+
18
+ // Load or create config
19
+ function loadConfig() {
20
+ try {
21
+ if (fs.existsSync(CONFIG_FILE)) {
22
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
23
+ }
24
+ } catch (err) {
25
+ console.error("Error loading config:", err.message);
26
+ }
27
+ return {};
28
+ }
29
+
30
+ function saveConfig(config) {
31
+ try {
32
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
33
+ } catch (err) {
34
+ console.error("Error saving config:", err.message);
35
+ }
36
+ }
37
+
38
+ function promptPassword() {
39
+ return new Promise((resolve) => {
40
+ const rl = readline.createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout
43
+ });
44
+
45
+ rl.question('Set a password for this device: ', (password) => {
46
+ rl.close();
47
+ resolve(password.trim());
48
+ });
49
+ });
50
+ }
51
+
52
+ const command = process.argv[2];
53
+
54
+ if (command === "login") {
55
+ (async () => {
56
+ const password = await promptPassword();
57
+ if (!password) {
58
+ console.log("Password is required");
59
+ return;
60
+ }
61
+
62
+ const config = loadConfig();
63
+ config.password = password;
64
+ saveConfig(config);
65
+
66
+ console.log("Password saved successfully!");
67
+ console.log("You can now run: vortix start");
68
+ console.log("\nIMPORTANT: Use this same password in the dashboard to access this device.");
69
+ })();
70
+ return;
71
+ }
72
+
73
+ if (command === "start") {
74
+ startAgent();
75
+ return;
76
+ }
77
+
78
+ console.log("Available commands:");
79
+ console.log(" vortix login - Set device password");
80
+ console.log(" vortix start - Start the agent");
81
+
82
+ function startAgent() {
83
+ const config = loadConfig();
84
+
85
+ if (!config.password) {
86
+ console.log("Please set a password first: vortix login");
87
+ return;
88
+ }
89
+
90
+ const deviceName = os.hostname();
91
+ const deviceToken = `device-${deviceName.toLowerCase()}`;
92
+ const token = `${deviceToken}:${config.password}`;
93
+
94
+ console.log(`Device: ${deviceName}`);
95
+ console.log("Connecting to backend...");
96
+
97
+ // Production backend URL - Render deployment
98
+ const BACKEND_URL = process.env.BACKEND_URL || 'wss://vortix.onrender.com';
99
+
100
+ const ws = new WebSocket(`${BACKEND_URL}?token=${encodeURIComponent(token)}`);
101
+
102
+ ws.on("open", () => {
103
+ console.log("Authenticated and connected to backend");
104
+
105
+ setInterval(() => {
106
+ if (ws.readyState === WebSocket.OPEN) {
107
+ console.log("Sending heartbeat...");
108
+ ws.send(
109
+ JSON.stringify({
110
+ type: "HEARTBEAT"
111
+ })
112
+ );
113
+ }
114
+ }, 5000);
115
+ });
116
+
117
+ ws.on("close", () => {
118
+ console.log("Disconnected from backend");
119
+ });
120
+
121
+ ws.on("error", (err) => {
122
+ console.error("WebSocket error:", err.message);
123
+ });
124
+
125
+ let commandQueue = [];
126
+ let isRunning = false;
127
+ let currentCwd = require("os").homedir(); // Track current working directory
128
+
129
+ ws.on("message", (message) => {
130
+ try {
131
+ const data = JSON.parse(message.toString());
132
+
133
+ if (data.type === "EXECUTE") {
134
+ console.log("Agent received EXECUTE:", data.command);
135
+ commandQueue.push(data.command);
136
+ processQueue();
137
+ }
138
+
139
+ } catch (err) {
140
+ console.error("Invalid message received:", message.toString());
141
+ }
142
+ });
143
+
144
+ function extractDirectoryFromCommand(command) {
145
+ // Check for cd commands and extract the target directory
146
+ // Handle: cd /d D:, cd /D D:\, cd D:\folder, etc.
147
+
148
+ // Match: cd /d D: or cd /D D: or similar
149
+ let match = command.match(/^\s*cd\s+\/[dD]\s+([^\s]+)\s*$/i);
150
+ if (match) {
151
+ let path = match[1].trim();
152
+ path = expandPath(path);
153
+ // Ensure it ends with backslash if it's just a drive letter
154
+ if (/^[A-Za-z]:$/.test(path)) {
155
+ path = path + "\\";
156
+ }
157
+ console.log("CD detected, new path:", path);
158
+ return path;
159
+ }
160
+
161
+ // Match: cd D:\folder or cd folder
162
+ match = command.match(/^\s*cd\s+([^\s][^\s]*)\s*$/i);
163
+ if (match) {
164
+ let path = match[1].trim();
165
+ path = expandPath(path);
166
+ // Only treat as cd if it looks like a path (has : or starts with \)
167
+ if (/[:\\]/.test(path)) {
168
+ if (/^[A-Za-z]:$/.test(path)) {
169
+ path = path + "\\";
170
+ }
171
+ console.log("CD detected, new path:", path);
172
+ return path;
173
+ }
174
+ }
175
+
176
+ return null;
177
+ }
178
+
179
+ function expandPath(path) {
180
+ // Expand environment variables
181
+ if (path.includes("%USERPROFILE%")) {
182
+ path = path.replace(/%USERPROFILE%/gi, os.homedir());
183
+ }
184
+ if (path.includes("%HOMEPATH%")) {
185
+ path = path.replace(/%HOMEPATH%/gi, os.homedir());
186
+ }
187
+ if (path.includes("%HOMEDRIVE%")) {
188
+ const homedir = os.homedir();
189
+ const drive = homedir.split("\\")[0];
190
+ path = path.replace(/%HOMEDRIVE%/gi, drive);
191
+ }
192
+ return path;
193
+ }
194
+
195
+ function processQueue() {
196
+ if (isRunning || commandQueue.length === 0) return;
197
+
198
+ isRunning = true;
199
+ let command = commandQueue.shift();
200
+
201
+ console.log("Executing command:", command);
202
+
203
+ // Update current working directory if this is a cd command
204
+ const newCwd = extractDirectoryFromCommand(command);
205
+ if (newCwd) {
206
+ currentCwd = newCwd;
207
+ ws.send(JSON.stringify({ type: "LOG", deviceName, message: `Directory changed to: ${currentCwd}` }));
208
+ console.log("Directory state updated to:", currentCwd);
209
+ }
210
+
211
+ // report current cwd for diagnostics
212
+ ws.send(JSON.stringify({ type: "LOG", deviceName, message: `Working from: ${currentCwd}` }));
213
+ ws.send(JSON.stringify({ type: "LOG", deviceName, message: `Executing: ${command}` }));
214
+
215
+ // Use cmd.exe with /c flag to properly execute Windows commands
216
+ const process = exec(command, {
217
+ cwd: currentCwd,
218
+ shell: "cmd.exe",
219
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer for large outputs
220
+ encoding: "utf8"
221
+ });
222
+
223
+ // Track if we've received any output
224
+ let hasOutput = false;
225
+
226
+ process.stdout.on("data", (data) => {
227
+ hasOutput = true;
228
+ ws.send(JSON.stringify({
229
+ type: "LOG",
230
+ deviceName,
231
+ message: data.toString()
232
+ }));
233
+ });
234
+
235
+ process.stderr.on("data", (data) => {
236
+ hasOutput = true;
237
+ ws.send(JSON.stringify({
238
+ type: "LOG",
239
+ deviceName,
240
+ message: `[ERROR] ${data.toString()}`
241
+ }));
242
+ });
243
+
244
+ process.on("error", (err) => {
245
+ ws.send(JSON.stringify({
246
+ type: "LOG",
247
+ deviceName,
248
+ message: `[EXEC ERROR] ${err.message}`
249
+ }));
250
+ });
251
+
252
+ process.on("close", (code) => {
253
+ ws.send(JSON.stringify({
254
+ type: "LOG",
255
+ deviceName,
256
+ message: `Command execution finished with code: ${code}`
257
+ }));
258
+
259
+ // Send structured result so server can orchestrate sequential steps
260
+ ws.send(JSON.stringify({
261
+ type: "EXECUTE_RESULT",
262
+ command,
263
+ code: typeof code === 'number' ? code : 0
264
+ }));
265
+
266
+ isRunning = false;
267
+ processQueue();
268
+ });
269
+ }
270
+ }
package/agent/auth.js ADDED
@@ -0,0 +1,32 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const readline = require("readline");
4
+
5
+ const CONFIG_PATH = path.join(__dirname, "config.json");
6
+
7
+ function login() {
8
+ const rl = readline.createInterface({
9
+ input: process.stdin,
10
+ output: process.stdout
11
+ });
12
+
13
+ rl.question("Enter device token: ", (token) => {
14
+ const config = { token: token.trim() };
15
+
16
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
17
+
18
+ console.log("Token saved successfully.");
19
+ rl.close();
20
+ });
21
+ }
22
+
23
+ function getToken() {
24
+ if (!fs.existsSync(CONFIG_PATH)) {
25
+ return null;
26
+ }
27
+
28
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH));
29
+ return config.token;
30
+ }
31
+
32
+ module.exports = { login, getToken };
@@ -0,0 +1,3 @@
1
+ {
2
+ "token": "device-test-device"
3
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "agent",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "agent",
9
+ "version": "1.0.0",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "ws": "^8.19.0"
13
+ }
14
+ },
15
+ "node_modules/ws": {
16
+ "version": "8.19.0",
17
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
18
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
19
+ "license": "MIT",
20
+ "engines": {
21
+ "node": ">=10.0.0"
22
+ },
23
+ "peerDependencies": {
24
+ "bufferutil": "^4.0.1",
25
+ "utf-8-validate": ">=5.0.2"
26
+ },
27
+ "peerDependenciesMeta": {
28
+ "bufferutil": {
29
+ "optional": true
30
+ },
31
+ "utf-8-validate": {
32
+ "optional": true
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "agent",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node agent.js",
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "type": "commonjs",
14
+ "dependencies": {
15
+ "ws": "^8.19.0"
16
+ }
17
+ }
@@ -0,0 +1,354 @@
1
+ {
2
+ "name": "vortix-backend",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "vortix-backend",
9
+ "version": "1.0.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "axios": "^1.13.5",
13
+ "ws": "^8.19.0"
14
+ },
15
+ "engines": {
16
+ "node": ">=14.0.0"
17
+ }
18
+ },
19
+ "node_modules/asynckit": {
20
+ "version": "0.4.0",
21
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
22
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
23
+ "license": "MIT"
24
+ },
25
+ "node_modules/axios": {
26
+ "version": "1.13.5",
27
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
28
+ "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "follow-redirects": "^1.15.11",
32
+ "form-data": "^4.0.5",
33
+ "proxy-from-env": "^1.1.0"
34
+ }
35
+ },
36
+ "node_modules/call-bind-apply-helpers": {
37
+ "version": "1.0.2",
38
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
39
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "es-errors": "^1.3.0",
43
+ "function-bind": "^1.1.2"
44
+ },
45
+ "engines": {
46
+ "node": ">= 0.4"
47
+ }
48
+ },
49
+ "node_modules/combined-stream": {
50
+ "version": "1.0.8",
51
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
52
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
53
+ "license": "MIT",
54
+ "dependencies": {
55
+ "delayed-stream": "~1.0.0"
56
+ },
57
+ "engines": {
58
+ "node": ">= 0.8"
59
+ }
60
+ },
61
+ "node_modules/delayed-stream": {
62
+ "version": "1.0.0",
63
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
64
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
65
+ "license": "MIT",
66
+ "engines": {
67
+ "node": ">=0.4.0"
68
+ }
69
+ },
70
+ "node_modules/dunder-proto": {
71
+ "version": "1.0.1",
72
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
73
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
74
+ "license": "MIT",
75
+ "dependencies": {
76
+ "call-bind-apply-helpers": "^1.0.1",
77
+ "es-errors": "^1.3.0",
78
+ "gopd": "^1.2.0"
79
+ },
80
+ "engines": {
81
+ "node": ">= 0.4"
82
+ }
83
+ },
84
+ "node_modules/es-define-property": {
85
+ "version": "1.0.1",
86
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
87
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
88
+ "license": "MIT",
89
+ "engines": {
90
+ "node": ">= 0.4"
91
+ }
92
+ },
93
+ "node_modules/es-errors": {
94
+ "version": "1.3.0",
95
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
96
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
97
+ "license": "MIT",
98
+ "engines": {
99
+ "node": ">= 0.4"
100
+ }
101
+ },
102
+ "node_modules/es-object-atoms": {
103
+ "version": "1.1.1",
104
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
105
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
106
+ "license": "MIT",
107
+ "dependencies": {
108
+ "es-errors": "^1.3.0"
109
+ },
110
+ "engines": {
111
+ "node": ">= 0.4"
112
+ }
113
+ },
114
+ "node_modules/es-set-tostringtag": {
115
+ "version": "2.1.0",
116
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
117
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
118
+ "license": "MIT",
119
+ "dependencies": {
120
+ "es-errors": "^1.3.0",
121
+ "get-intrinsic": "^1.2.6",
122
+ "has-tostringtag": "^1.0.2",
123
+ "hasown": "^2.0.2"
124
+ },
125
+ "engines": {
126
+ "node": ">= 0.4"
127
+ }
128
+ },
129
+ "node_modules/follow-redirects": {
130
+ "version": "1.15.11",
131
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
132
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
133
+ "funding": [
134
+ {
135
+ "type": "individual",
136
+ "url": "https://github.com/sponsors/RubenVerborgh"
137
+ }
138
+ ],
139
+ "license": "MIT",
140
+ "engines": {
141
+ "node": ">=4.0"
142
+ },
143
+ "peerDependenciesMeta": {
144
+ "debug": {
145
+ "optional": true
146
+ }
147
+ }
148
+ },
149
+ "node_modules/form-data": {
150
+ "version": "4.0.5",
151
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
152
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
153
+ "license": "MIT",
154
+ "dependencies": {
155
+ "asynckit": "^0.4.0",
156
+ "combined-stream": "^1.0.8",
157
+ "es-set-tostringtag": "^2.1.0",
158
+ "hasown": "^2.0.2",
159
+ "mime-types": "^2.1.12"
160
+ },
161
+ "engines": {
162
+ "node": ">= 6"
163
+ }
164
+ },
165
+ "node_modules/function-bind": {
166
+ "version": "1.1.2",
167
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
168
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
169
+ "license": "MIT",
170
+ "funding": {
171
+ "url": "https://github.com/sponsors/ljharb"
172
+ }
173
+ },
174
+ "node_modules/get-intrinsic": {
175
+ "version": "1.3.0",
176
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
177
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
178
+ "license": "MIT",
179
+ "dependencies": {
180
+ "call-bind-apply-helpers": "^1.0.2",
181
+ "es-define-property": "^1.0.1",
182
+ "es-errors": "^1.3.0",
183
+ "es-object-atoms": "^1.1.1",
184
+ "function-bind": "^1.1.2",
185
+ "get-proto": "^1.0.1",
186
+ "gopd": "^1.2.0",
187
+ "has-symbols": "^1.1.0",
188
+ "hasown": "^2.0.2",
189
+ "math-intrinsics": "^1.1.0"
190
+ },
191
+ "engines": {
192
+ "node": ">= 0.4"
193
+ },
194
+ "funding": {
195
+ "url": "https://github.com/sponsors/ljharb"
196
+ }
197
+ },
198
+ "node_modules/get-proto": {
199
+ "version": "1.0.1",
200
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
201
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
202
+ "license": "MIT",
203
+ "dependencies": {
204
+ "dunder-proto": "^1.0.1",
205
+ "es-object-atoms": "^1.0.0"
206
+ },
207
+ "engines": {
208
+ "node": ">= 0.4"
209
+ }
210
+ },
211
+ "node_modules/gopd": {
212
+ "version": "1.2.0",
213
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
214
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
215
+ "license": "MIT",
216
+ "engines": {
217
+ "node": ">= 0.4"
218
+ },
219
+ "funding": {
220
+ "url": "https://github.com/sponsors/ljharb"
221
+ }
222
+ },
223
+ "node_modules/has-symbols": {
224
+ "version": "1.1.0",
225
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
226
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
227
+ "license": "MIT",
228
+ "engines": {
229
+ "node": ">= 0.4"
230
+ },
231
+ "funding": {
232
+ "url": "https://github.com/sponsors/ljharb"
233
+ }
234
+ },
235
+ "node_modules/has-tostringtag": {
236
+ "version": "1.0.2",
237
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
238
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
239
+ "license": "MIT",
240
+ "dependencies": {
241
+ "has-symbols": "^1.0.3"
242
+ },
243
+ "engines": {
244
+ "node": ">= 0.4"
245
+ },
246
+ "funding": {
247
+ "url": "https://github.com/sponsors/ljharb"
248
+ }
249
+ },
250
+ "node_modules/hasown": {
251
+ "version": "2.0.2",
252
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
253
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
254
+ "license": "MIT",
255
+ "dependencies": {
256
+ "function-bind": "^1.1.2"
257
+ },
258
+ "engines": {
259
+ "node": ">= 0.4"
260
+ }
261
+ },
262
+ "node_modules/math-intrinsics": {
263
+ "version": "1.1.0",
264
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
265
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
266
+ "license": "MIT",
267
+ "engines": {
268
+ "node": ">= 0.4"
269
+ }
270
+ },
271
+ "node_modules/mime-db": {
272
+ "version": "1.52.0",
273
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
274
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
275
+ "license": "MIT",
276
+ "engines": {
277
+ "node": ">= 0.6"
278
+ }
279
+ },
280
+ "node_modules/mime-types": {
281
+ "version": "2.1.35",
282
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
283
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
284
+ "license": "MIT",
285
+ "dependencies": {
286
+ "mime-db": "1.52.0"
287
+ },
288
+ "engines": {
289
+ "node": ">= 0.6"
290
+ }
291
+ },
292
+ "node_modules/openai": {
293
+ "version": "6.18.0",
294
+ "resolved": "https://registry.npmjs.org/openai/-/openai-6.18.0.tgz",
295
+ "integrity": "sha512-odLRYyz9rlzz6g8gKn61RM2oP5UUm428sE2zOxZqS9MzVfD5/XW8UoEjpnRkzTuScXP7ZbP/m7fC+bl8jCOZZw==",
296
+ "license": "Apache-2.0",
297
+ "bin": {
298
+ "openai": "bin/cli"
299
+ },
300
+ "peerDependencies": {
301
+ "ws": "^8.18.0",
302
+ "zod": "^3.25 || ^4.0"
303
+ },
304
+ "peerDependenciesMeta": {
305
+ "ws": {
306
+ "optional": true
307
+ },
308
+ "zod": {
309
+ "optional": true
310
+ }
311
+ }
312
+ },
313
+ "node_modules/proxy-from-env": {
314
+ "version": "1.1.0",
315
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
316
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
317
+ "license": "MIT"
318
+ },
319
+ "node_modules/uuid": {
320
+ "version": "13.0.0",
321
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
322
+ "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
323
+ "funding": [
324
+ "https://github.com/sponsors/broofa",
325
+ "https://github.com/sponsors/ctavan"
326
+ ],
327
+ "license": "MIT",
328
+ "bin": {
329
+ "uuid": "dist-node/bin/uuid"
330
+ }
331
+ },
332
+ "node_modules/ws": {
333
+ "version": "8.19.0",
334
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
335
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
336
+ "license": "MIT",
337
+ "engines": {
338
+ "node": ">=10.0.0"
339
+ },
340
+ "peerDependencies": {
341
+ "bufferutil": "^4.0.1",
342
+ "utf-8-validate": ">=5.0.2"
343
+ },
344
+ "peerDependenciesMeta": {
345
+ "bufferutil": {
346
+ "optional": true
347
+ },
348
+ "utf-8-validate": {
349
+ "optional": true
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "vortix-backend",
3
+ "version": "1.0.0",
4
+ "description": "Vortix WebSocket backend server",
5
+ "main": "server.js",
6
+ "scripts": {
7
+ "start": "node server.js",
8
+ "dev": "node server.js"
9
+ },
10
+ "keywords": ["websocket", "remote-control", "ai"],
11
+ "author": "",
12
+ "license": "MIT",
13
+ "type": "commonjs",
14
+ "engines": {
15
+ "node": ">=14.0.0"
16
+ },
17
+ "dependencies": {
18
+ "axios": "^1.13.5",
19
+ "ws": "^8.19.0"
20
+ }
21
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://railway.app/railway.schema.json",
3
+ "build": {
4
+ "builder": "NIXPACKS"
5
+ },
6
+ "deploy": {
7
+ "startCommand": "node server.js",
8
+ "restartPolicyType": "ON_FAILURE",
9
+ "restartPolicyMaxRetries": 10
10
+ }
11
+ }
@@ -0,0 +1,500 @@
1
+ const WebSocket = require("ws");
2
+ const readline = require("readline");
3
+ const axios = require("axios");
4
+ const os = require("os");
5
+ const path = require("path");
6
+
7
+ // Use environment PORT for cloud deployment, fallback to 8080 for local
8
+ const PORT = process.env.PORT || 8080;
9
+
10
+ const dashboardClients = new Set();
11
+ const devices = new Map();
12
+ // pendingResults holds awaiting resolvers for EXECUTE_RESULT from agents
13
+ const pendingResults = new Map(); // deviceName -> [{ command, resolve, reject, timer }]
14
+
15
+ const wss = new WebSocket.Server({ port: PORT });
16
+
17
+ console.log(`Backend running on port ${PORT}`);
18
+
19
+ function waitForExecuteResult(deviceName, command, timeoutMs = 120000) {
20
+ return new Promise((resolve, reject) => {
21
+ const list = pendingResults.get(deviceName) || [];
22
+ const entry = { command, resolve, reject, timer: null };
23
+ // timeout
24
+ entry.timer = setTimeout(() => {
25
+ // remove entry
26
+ const arr = pendingResults.get(deviceName) || [];
27
+ const idx = arr.indexOf(entry);
28
+ if (idx !== -1) arr.splice(idx, 1);
29
+ pendingResults.set(deviceName, arr);
30
+ reject(new Error('Timed out waiting for EXECUTE_RESULT'));
31
+ }, timeoutMs);
32
+
33
+ list.push(entry);
34
+ pendingResults.set(deviceName, list);
35
+ });
36
+ }
37
+
38
+ // ---------- WebSocket Handling ----------
39
+ wss.on("connection", (ws, req) => {
40
+ const url = new URL(req.url, "http://localhost");
41
+
42
+ const token = url.searchParams.get("token");
43
+ const clientType = url.searchParams.get("type");
44
+
45
+ // ===== DASHBOARD CONNECTION =====
46
+ if (clientType === "dashboard") {
47
+ dashboardClients.add(ws);
48
+ console.log("Dashboard connected");
49
+ // Send initial device list to newly connected dashboard
50
+ broadcastDevices();
51
+
52
+ ws.on("message", async (message) => {
53
+ const data = JSON.parse(message);
54
+ if (data.type === "FORCE_EXECUTE") {
55
+ const targetDevice = [...devices.values()].find(
56
+ (d) =>
57
+ d.deviceName === data.deviceName &&
58
+ d.status === "online"
59
+ );
60
+
61
+ if (!targetDevice) return;
62
+
63
+ targetDevice.ws.send(
64
+ JSON.stringify({
65
+ type: "EXECUTE",
66
+ command: data.command,
67
+ })
68
+ );
69
+
70
+ console.log("Force executed dangerous command");
71
+ }
72
+
73
+ // if (data.type === "COMMAND") {
74
+ // const targetDevice = [...devices.values()].find(
75
+ // (d) =>
76
+ // d.deviceName === data.deviceName &&
77
+ // d.status === "online"
78
+ // );
79
+
80
+ // if (!targetDevice) {
81
+ // console.log("Target device not found or offline");
82
+ // return;
83
+ // }
84
+ // if (isDangerousCommand(data.command)) {
85
+ // ws.send(
86
+ // JSON.stringify({
87
+ // type: "APPROVAL_REQUIRED",
88
+ // deviceName: data.deviceName,
89
+ // command: data.command
90
+ // })
91
+ // );
92
+
93
+ // console.log("Dangerous command detected, approval required");
94
+ // return;
95
+ // }
96
+
97
+ // targetDevice.ws.send(
98
+ // JSON.stringify({
99
+ // type: "EXECUTE",
100
+ // command: data.command,
101
+ // })
102
+ // );
103
+
104
+ // console.log(
105
+ // `Dashboard sent command to ${data.deviceName}`
106
+ // );
107
+ // }
108
+ if (data.type === "APPROVE_PLAN") {
109
+ const targetDevice = [...devices.values()].find(
110
+ (d) =>
111
+ d.deviceName === data.deviceName &&
112
+ d.status === "online"
113
+ );
114
+
115
+ if (!targetDevice) {
116
+ console.log("Target device not found or offline");
117
+ dashboardClients.forEach((client) => {
118
+ client.send(
119
+ JSON.stringify({
120
+ type: "EXECUTION_FINISHED",
121
+ deviceName: data.deviceName,
122
+ error: "Device not found or offline"
123
+ })
124
+ );
125
+ });
126
+ return;
127
+ }
128
+
129
+ console.log("APPROVE_PLAN received:", data.steps);
130
+
131
+ // Notify all dashboard clients execution started
132
+ dashboardClients.forEach((client) => {
133
+ client.send(
134
+ JSON.stringify({
135
+ type: "EXECUTION_STARTED",
136
+ deviceName: data.deviceName
137
+ })
138
+ );
139
+ });
140
+
141
+ // Execute steps sequentially: wait for agent to report EXECUTE_RESULT for each step
142
+ (async () => {
143
+ let aborted = false;
144
+ for (const step of data.steps) {
145
+ // Send command EXACTLY as provided - don't modify it
146
+ const commandToExecute = step.command || step;
147
+ console.log("Sending EXECUTE to agent for:", commandToExecute);
148
+
149
+ targetDevice.ws.send(
150
+ JSON.stringify({
151
+ type: "EXECUTE",
152
+ command: commandToExecute
153
+ })
154
+ );
155
+
156
+ try {
157
+ const result = await waitForExecuteResult(targetDevice.deviceName, commandToExecute);
158
+ console.log("Step result:", result);
159
+
160
+ // send step log to dashboards
161
+ dashboardClients.forEach((client) => {
162
+ client.send(
163
+ JSON.stringify({
164
+ type: "LOG",
165
+ deviceName: targetDevice.deviceName,
166
+ message: `✓ Step completed: ${commandToExecute} (exit code: ${result.code})`
167
+ })
168
+ );
169
+ });
170
+
171
+ // Continue even if code is not 0 - let user see the output
172
+ // but notify about non-zero exit
173
+ if (typeof result.code === 'number' && result.code !== 0) {
174
+ console.log(`Step exited with code ${result.code}: ${commandToExecute}`);
175
+ }
176
+ } catch (err) {
177
+ console.error("Step execution error:", err.message);
178
+ dashboardClients.forEach((client) => {
179
+ client.send(
180
+ JSON.stringify({
181
+ type: "LOG",
182
+ deviceName: targetDevice.deviceName,
183
+ message: `✗ Step failed: ${commandToExecute} - ${err.message}`
184
+ })
185
+ );
186
+ });
187
+ dashboardClients.forEach((client) => {
188
+ client.send(
189
+ JSON.stringify({
190
+ type: "EXECUTION_FINISHED",
191
+ deviceName: data.deviceName,
192
+ error: err.message
193
+ })
194
+ );
195
+ });
196
+ aborted = true;
197
+ break;
198
+ }
199
+ }
200
+
201
+ if (!aborted) {
202
+ dashboardClients.forEach((client) => {
203
+ client.send(
204
+ JSON.stringify({
205
+ type: "EXECUTION_FINISHED",
206
+ deviceName: data.deviceName,
207
+ success: true
208
+ })
209
+ );
210
+ });
211
+ }
212
+ })();
213
+ }
214
+
215
+ if (data.type === "PLAN") {
216
+
217
+ const targetDevice = [...devices.values()].find(
218
+ (d) =>
219
+ d.deviceName === data.deviceName &&
220
+ d.status === "online"
221
+ );
222
+
223
+ if (!targetDevice) {
224
+ console.log("Device not found or offline");
225
+ return;
226
+ }
227
+
228
+ try {
229
+ const plan = await generatePlan(
230
+ data.command,
231
+ targetDevice.platform || "win32"
232
+ );
233
+ console.log("Generated plan:", plan.steps);
234
+
235
+ // SEND PLAN PREVIEW TO DASHBOARD - wait for approval
236
+ ws.send(
237
+ JSON.stringify({
238
+ type: "PLAN_PREVIEW",
239
+ deviceName: data.deviceName,
240
+ steps: plan.steps
241
+ })
242
+ );
243
+
244
+ console.log("AI Plan sent for approval:", plan);
245
+ } catch (err) {
246
+ console.error("AI planning error:", err.message);
247
+ // Notify dashboard of error
248
+ dashboardClients.forEach((client) => {
249
+ client.send(
250
+ JSON.stringify({
251
+ type: "EXECUTION_FINISHED",
252
+ deviceName: data.deviceName,
253
+ error: err.message
254
+ })
255
+ );
256
+ });
257
+ }
258
+ }
259
+
260
+ });
261
+
262
+ ws.on("close", () => {
263
+ dashboardClients.delete(ws);
264
+ console.log("Dashboard disconnected");
265
+ });
266
+
267
+ return;
268
+ }
269
+
270
+
271
+ // ===== AGENT CONNECTION =====
272
+ if (!token || !devices.has(token)) {
273
+ console.log("Unauthorized connection attempt");
274
+ ws.close();
275
+ return;
276
+ }
277
+
278
+ const device = devices.get(token);
279
+
280
+ device.ws = ws;
281
+ device.status = "online";
282
+ device.lastSeen = Date.now();
283
+
284
+ console.log(`Authenticated device: ${device.deviceName}`);
285
+
286
+ broadcastDevices(); // 🔥 notify dashboard
287
+
288
+ ws.on("message", (message) => {
289
+ const data = JSON.parse(message);
290
+
291
+ if (data.type === "HEARTBEAT") {
292
+ device.lastSeen = Date.now();
293
+ console.log(`Heartbeat received from ${device.deviceName}`);
294
+ }
295
+
296
+ if (data.type === "LOG") {
297
+ console.log(`[${device.deviceName}] ${data.message}`);
298
+
299
+ // 🔥 send logs to dashboard
300
+ dashboardClients.forEach((client) => {
301
+ client.send(
302
+ JSON.stringify({
303
+ type: "LOG",
304
+ deviceName: device.deviceName,
305
+ message: data.message,
306
+ })
307
+ );
308
+ });
309
+ }
310
+
311
+ if (data.type === "EXECUTE_RESULT") {
312
+ console.log(`EXECUTE_RESULT received from ${device.deviceName}:`, data);
313
+ const list = pendingResults.get(device.deviceName) || [];
314
+ for (let i = 0; i < list.length; i++) {
315
+ const entry = list[i];
316
+ if (entry.command === data.command) {
317
+ clearTimeout(entry.timer);
318
+ try {
319
+ entry.resolve(data);
320
+ } catch (e) {
321
+ entry.reject(e);
322
+ }
323
+ list.splice(i, 1);
324
+ break;
325
+ }
326
+ }
327
+ pendingResults.set(device.deviceName, list);
328
+ }
329
+ });
330
+
331
+ ws.on("close", () => {
332
+ device.status = "offline";
333
+ console.log(`${device.deviceName} disconnected`);
334
+ broadcastDevices(); // 🔥 notify dashboard
335
+ });
336
+ });
337
+
338
+ // ---------- Device Registration ----------
339
+ function registerDevice(deviceName, token) {
340
+ // If no token provided, use the hostname-based token for consistency
341
+ if (!token) {
342
+ token = `device-${deviceName.toLowerCase()}`;
343
+ }
344
+
345
+ devices.set(token, {
346
+ deviceName,
347
+ status: "offline",
348
+ ws: null,
349
+ lastSeen: null
350
+ });
351
+
352
+ console.log(`Registered device: ${deviceName}`);
353
+ console.log(`Token: ${token}`);
354
+
355
+ return token;
356
+ }
357
+
358
+ // Register one device manually for testing
359
+ registerDevice("Test-Device");
360
+ registerDevice("VAIBHAV-PC");
361
+
362
+ // ---------- Heartbeat Monitor ----------
363
+ setInterval(() => {
364
+ const now = Date.now();
365
+
366
+ devices.forEach((device) => {
367
+ if (
368
+ device.status === "online" &&
369
+ device.lastSeen &&
370
+ now - device.lastSeen > 15000
371
+ ) {
372
+ device.status = "offline";
373
+ console.log(`${device.deviceName} marked offline`);
374
+ }
375
+ });
376
+ }, 5000);
377
+
378
+ // ---------- Terminal Command Interface (only in local development) ----------
379
+ if (process.env.NODE_ENV !== 'production' && !process.env.RAILWAY_ENVIRONMENT) {
380
+ const rl = readline.createInterface({
381
+ input: process.stdin,
382
+ output: process.stdout
383
+ });
384
+
385
+ console.log("Type: send <DeviceName> <command>");
386
+
387
+ rl.on("line", (input) => {
388
+ const [keyword, deviceName, ...cmdParts] = input.split(" ");
389
+ const command = cmdParts.join(" ");
390
+
391
+ if (keyword !== "send") {
392
+ console.log("Invalid command. Use: send <DeviceName> <command>");
393
+ return;
394
+ }
395
+
396
+ let targetDevice = null;
397
+
398
+ devices.forEach((device) => {
399
+ if (device.deviceName === deviceName && device.status === "online") {
400
+ targetDevice = device;
401
+ }
402
+ });
403
+
404
+ if (!targetDevice) {
405
+ console.log("Device not found or offline.");
406
+ return;
407
+ }
408
+
409
+ targetDevice.ws.send(
410
+ JSON.stringify({
411
+ type: "EXECUTE",
412
+ command
413
+ })
414
+ );
415
+
416
+ console.log(`Command sent to ${deviceName}`);
417
+ });
418
+ } else {
419
+ console.log("Running in production mode - terminal interface disabled");
420
+ }
421
+
422
+ async function generatePlan(userInput, platform) {
423
+ const homeDir = os.homedir();
424
+ const desktopPath = path.join(homeDir, "Desktop");
425
+
426
+ const prompt = `
427
+ You are an AI OS command planner. Generate EXACT Windows commands for the user's request.
428
+
429
+ System Information:
430
+ - Home Directory: ${homeDir}
431
+ - Desktop Path: ${desktopPath}
432
+ - Platform: ${platform}
433
+
434
+ CRITICAL RULES:
435
+ 1. Return ONLY valid JSON in this format - nothing else
436
+ 2. Use ABSOLUTE paths, NOT relative paths
437
+ 3. Use echo command for file creation (NOT type, NOT powershell)
438
+ 4. One command per step
439
+ 5. Never create unnecessary directories before files
440
+ 6. Format: echo "content" > full_path_to_file.txt
441
+
442
+ CORRECT Examples:
443
+ - "create hello.txt with html on desktop"
444
+ Response: {"steps": [{"command": "echo <!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello</h1></body></html> > ${desktopPath}\\hello.txt"}]}
445
+
446
+ - "create test file with hello world"
447
+ Response: {"steps": [{"command": "echo hello world > ${homeDir}\\Desktop\\test.txt"}]}
448
+
449
+ - "list desktop files"
450
+ Response: {"steps": [{"command": "dir ${desktopPath}"}]}
451
+
452
+ User Request: ${userInput}
453
+
454
+ Return ONLY JSON:
455
+ `;
456
+
457
+ const response = await axios.post(
458
+ "http://localhost:11434/api/generate",
459
+ {
460
+ model: "qwen2.5:7b",
461
+ prompt: prompt,
462
+ stream: false
463
+ }
464
+ );
465
+
466
+ const text = response.data.response.trim();
467
+
468
+ console.log("LLM raw output:", text);
469
+
470
+ // Extract JSON safely
471
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
472
+
473
+ if (!jsonMatch) {
474
+ throw new Error("No valid JSON found in LLM output");
475
+ }
476
+
477
+ return JSON.parse(jsonMatch[0]);
478
+ }
479
+
480
+
481
+
482
+ function broadcastDevices() {
483
+ const deviceList = [];
484
+
485
+ devices.forEach((device) => {
486
+ deviceList.push({
487
+ deviceName: device.deviceName,
488
+ status: device.status,
489
+ });
490
+ });
491
+
492
+ dashboardClients.forEach((client) => {
493
+ client.send(
494
+ JSON.stringify({
495
+ type: "DEVICES",
496
+ devices: deviceList,
497
+ })
498
+ );
499
+ });
500
+ }
package/bin/vortix.js CHANGED
@@ -1,15 +1,13 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
- const { spawn } = require('child_process');
4
3
  const path = require('path');
5
- const os = require('os');
6
4
 
7
5
  const command = process.argv[2];
8
- const args = process.argv.slice(3);
9
6
 
10
- // Paths to agent and backend
11
- const agentPath = path.join(__dirname, '../../agent/agent.js');
12
- const backendPath = path.join(__dirname, '../../backend/server.js');
7
+ // When installed via npm, files are in the package directory
8
+ const packageRoot = path.join(__dirname, '..');
9
+ const agentPath = path.join(packageRoot, 'agent', 'agent.js');
10
+ const backendPath = path.join(packageRoot, 'backend', 'server.js');
13
11
 
14
12
  function showHelp() {
15
13
  console.log(`
@@ -29,28 +27,34 @@ Examples:
29
27
  }
30
28
 
31
29
  function runAgent(cmd) {
32
- const proc = spawn('node', [agentPath, cmd], {
33
- stdio: 'inherit',
34
- cwd: path.dirname(agentPath)
35
- });
30
+ // Set the command argument for the agent
31
+ process.argv[2] = cmd;
36
32
 
37
- proc.on('error', (err) => {
33
+ // Change to agent directory
34
+ process.chdir(path.dirname(agentPath));
35
+
36
+ // Require and run the agent
37
+ try {
38
+ require(agentPath);
39
+ } catch (err) {
38
40
  console.error('Failed to start agent:', err.message);
39
41
  process.exit(1);
40
- });
42
+ }
41
43
  }
42
44
 
43
45
  function runBackend() {
44
46
  console.log('Starting Vortix backend server...');
45
- const proc = spawn('node', [backendPath], {
46
- stdio: 'inherit',
47
- cwd: path.dirname(backendPath)
48
- });
49
47
 
50
- proc.on('error', (err) => {
48
+ // Change to backend directory
49
+ process.chdir(path.dirname(backendPath));
50
+
51
+ // Require and run the backend
52
+ try {
53
+ require(backendPath);
54
+ } catch (err) {
51
55
  console.error('Failed to start backend:', err.message);
52
56
  process.exit(1);
53
- });
57
+ }
54
58
  }
55
59
 
56
60
  switch (command) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vortix",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "AI-powered OS control system with remote command execution",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -14,25 +14,23 @@
14
14
  "os-control",
15
15
  "agent"
16
16
  ],
17
- "author": "Your Name <your.email@example.com>",
17
+ "author": "Vaibhav Rajpoot <vaibhavrajpoot2626@gmail.com>",
18
18
  "license": "MIT",
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "https://github.com/yourusername/vortix.git"
21
+ "url": "https://github.com/Vaibhav262610/vortix.git"
22
22
  },
23
23
  "engines": {
24
24
  "node": ">=14.0.0"
25
25
  },
26
26
  "files": [
27
27
  "bin/",
28
- "../agent/",
29
- "../backend/",
28
+ "agent/",
29
+ "backend/",
30
30
  "README.md"
31
31
  ],
32
32
  "dependencies": {
33
33
  "axios": "^1.6.0",
34
- "ws": "^8.19.0",
35
- "uuid": "^13.0.0",
36
- "openai": "^6.18.0"
34
+ "ws": "^8.19.0"
37
35
  }
38
36
  }