npm-noxyai 1.0.16 → 1.0.17
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/index-noxyai.js +53 -20
- package/package.json +1 -1
package/index-noxyai.js
CHANGED
|
@@ -8,6 +8,7 @@ const readline = require('readline');
|
|
|
8
8
|
|
|
9
9
|
const BASE_URL = 'https://www.noxyai.com';
|
|
10
10
|
const CONFIG_FILE = path.join(os.homedir(), '.noxyai.json');
|
|
11
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11
12
|
|
|
12
13
|
const args = process.argv.slice(2);
|
|
13
14
|
const command = args[0];
|
|
@@ -54,12 +55,10 @@ async function login() {
|
|
|
54
55
|
const { deviceCode, verificationUrl, interval } = await initRes.json();
|
|
55
56
|
console.log(`URL: ${verificationUrl}\n`);
|
|
56
57
|
|
|
57
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
58
58
|
rl.question('Press ENTER to automatically open the browser...', () => {
|
|
59
59
|
const platform = os.platform();
|
|
60
60
|
if (platform === 'android') spawn('termux-open-url', [verificationUrl], { stdio: 'ignore' });
|
|
61
61
|
else spawn('xdg-open', [verificationUrl], { stdio: 'ignore' });
|
|
62
|
-
rl.close();
|
|
63
62
|
startSpinner('Waiting for authentication...');
|
|
64
63
|
});
|
|
65
64
|
|
|
@@ -79,6 +78,8 @@ async function login() {
|
|
|
79
78
|
} catch (error) { console.error('Failed to connect.', error.message); }
|
|
80
79
|
}
|
|
81
80
|
|
|
81
|
+
const askConfirm = (query) => new Promise(resolve => rl.question(query, resolve));
|
|
82
|
+
|
|
82
83
|
function runTerminalCommand(cmd) {
|
|
83
84
|
return new Promise((resolve, reject) => {
|
|
84
85
|
console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
|
|
@@ -122,6 +123,10 @@ async function chat(prompt, depth = 0) {
|
|
|
122
123
|
const reader = res.body.getReader();
|
|
123
124
|
const decoder = new TextDecoder('utf-8');
|
|
124
125
|
let fullResponse = "";
|
|
126
|
+
|
|
127
|
+
// UI State for Boxed Reasoning
|
|
128
|
+
let isFirstReasoning = true;
|
|
129
|
+
let reasoningClosed = false;
|
|
125
130
|
|
|
126
131
|
while (true) {
|
|
127
132
|
const { done, value } = await reader.read();
|
|
@@ -133,26 +138,51 @@ async function chat(prompt, depth = 0) {
|
|
|
133
138
|
if (data === '[DONE]') break;
|
|
134
139
|
try {
|
|
135
140
|
const parsed = JSON.parse(data);
|
|
141
|
+
|
|
142
|
+
// 🚨 BOXED REASONING UI
|
|
136
143
|
if (parsed.isReasoning) {
|
|
137
|
-
|
|
144
|
+
if (isFirstReasoning) {
|
|
145
|
+
process.stdout.write('\n\x1b[35m┌── 🧠 NoxyAI Deep Analysis ────────────────\x1b[0m\n\x1b[31m│ \x1b[0m');
|
|
146
|
+
isFirstReasoning = false;
|
|
147
|
+
}
|
|
148
|
+
const text = parsed.text.replace(/\n/g, '\n\x1b[31m│ \x1b[0m');
|
|
149
|
+
process.stdout.write('\x1b[31m' + text + '\x1b[0m');
|
|
138
150
|
} else if (parsed.text) {
|
|
151
|
+
if (!isFirstReasoning && !reasoningClosed) {
|
|
152
|
+
process.stdout.write('\n\x1b[35m└───────────────────────────────────────────\x1b[0m\n\n');
|
|
153
|
+
reasoningClosed = true;
|
|
154
|
+
}
|
|
139
155
|
fullResponse += parsed.text;
|
|
140
156
|
process.stdout.write(parsed.text);
|
|
141
157
|
} else if (parsed.cost) {
|
|
142
|
-
|
|
143
|
-
console.log(`\n\n\x1b[33m💳 Tokens Used: ${parsed.tokens} | Credits Deducted: ${parsed.cost}\x1b[0m`);
|
|
158
|
+
console.log(`\n\n\x1b[33m💳 Tokens Used: ${parsed.tokens} | Low-Cost Credits Deducted: ${parsed.cost}\x1b[0m`);
|
|
144
159
|
}
|
|
145
160
|
} catch (e) {}
|
|
146
161
|
}
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
164
|
|
|
165
|
+
if (!isFirstReasoning && !reasoningClosed) {
|
|
166
|
+
process.stdout.write('\n\x1b[35m└───────────────────────────────────────────\x1b[0m\n\n');
|
|
167
|
+
}
|
|
168
|
+
|
|
150
169
|
if (!config.agentMode) { console.log(); return; }
|
|
151
170
|
|
|
152
171
|
console.log('\n\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
153
172
|
let agentFeedback = "";
|
|
154
173
|
|
|
155
|
-
|
|
174
|
+
// Tool: Push Notifications
|
|
175
|
+
const notifyRegex = /<notify>([\s\S]*?)<\/notify>/g; let match;
|
|
176
|
+
while ((match = notifyRegex.exec(fullResponse)) !== null) {
|
|
177
|
+
const msg = match[1].trim();
|
|
178
|
+
console.log(`\x1b[32m🔔 Notification Sent:\x1b[0m ${msg}`);
|
|
179
|
+
const platform = os.platform();
|
|
180
|
+
if (platform === 'android') spawn('termux-notification', ['-c', msg]);
|
|
181
|
+
else if (platform === 'darwin') spawn('osascript', ['-e', `display notification "${msg}" with title "NoxyAI"`]);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Tool: Read
|
|
185
|
+
const readRegex = /<read>([\s\S]*?)<\/read>/g;
|
|
156
186
|
while ((match = readRegex.exec(fullResponse)) !== null) {
|
|
157
187
|
const filePath = match[1].trim();
|
|
158
188
|
try {
|
|
@@ -162,6 +192,7 @@ async function chat(prompt, depth = 0) {
|
|
|
162
192
|
} catch (err) { agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`; }
|
|
163
193
|
}
|
|
164
194
|
|
|
195
|
+
// Tool: Search
|
|
165
196
|
const searchRegex = /<search>([\s\S]*?)<\/search>/g;
|
|
166
197
|
while ((match = searchRegex.exec(fullResponse)) !== null) {
|
|
167
198
|
const query = match[1].trim();
|
|
@@ -172,16 +203,10 @@ async function chat(prompt, depth = 0) {
|
|
|
172
203
|
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
|
|
173
204
|
body: JSON.stringify({ query })
|
|
174
205
|
});
|
|
175
|
-
|
|
176
|
-
// 🚨 SAFE JSON PARSING TO PREVENT HTML CRASHES
|
|
177
206
|
const responseText = await searchRes.text();
|
|
178
207
|
let searchData;
|
|
179
|
-
try {
|
|
180
|
-
|
|
181
|
-
} catch (parseError) {
|
|
182
|
-
throw new Error("Search backend returned an invalid response (Likely a 500 error or missing Vercel environment variables).");
|
|
183
|
-
}
|
|
184
|
-
|
|
208
|
+
try { searchData = JSON.parse(responseText); }
|
|
209
|
+
catch (e) { throw new Error("Search backend failed."); }
|
|
185
210
|
if (!searchRes.ok) throw new Error(searchData.error || "Search failed");
|
|
186
211
|
|
|
187
212
|
let snippets = "No results found.";
|
|
@@ -195,10 +220,11 @@ async function chat(prompt, depth = 0) {
|
|
|
195
220
|
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n`;
|
|
196
221
|
} catch (err) {
|
|
197
222
|
console.log(`\x1b[31m❌ Search Failed:\x1b[0m ${err.message}`);
|
|
198
|
-
agentFeedback += `\n[SEARCH FAILED: ${err.message}
|
|
223
|
+
agentFeedback += `\n[SEARCH FAILED: ${err.message}]\n`;
|
|
199
224
|
}
|
|
200
225
|
}
|
|
201
226
|
|
|
227
|
+
// Tool: Write
|
|
202
228
|
const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
|
|
203
229
|
while ((match = fileRegex.exec(fullResponse)) !== null) {
|
|
204
230
|
const filePath = match[1], content = match[2];
|
|
@@ -208,17 +234,25 @@ async function chat(prompt, depth = 0) {
|
|
|
208
234
|
console.log(`\x1b[32m✔ Wrote file:\x1b[0m ${filePath}`);
|
|
209
235
|
}
|
|
210
236
|
|
|
237
|
+
// 🚨 SAFE EXECUTION LAYER
|
|
211
238
|
const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
|
|
212
239
|
const commands = [];
|
|
213
240
|
while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
|
|
241
|
+
|
|
214
242
|
for (const cmd of commands) {
|
|
243
|
+
const answer = await askConfirm(`\n\x1b[33m⚠️ Agent wants to run:\x1b[0m \x1b[36m${cmd}\x1b[0m\nAllow? [Y/n]: `);
|
|
244
|
+
if (answer.toLowerCase() === 'n') {
|
|
245
|
+
console.log('\x1b[31mCommand skipped by user.\x1b[0m');
|
|
246
|
+
agentFeedback += `\n[USER DENIED COMMAND: ${cmd}]\n`;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
215
249
|
try { await runTerminalCommand(cmd); }
|
|
216
250
|
catch (err) { console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`); agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`; }
|
|
217
251
|
}
|
|
218
252
|
|
|
219
253
|
if (agentFeedback) {
|
|
220
254
|
console.log(`\x1b[33m🔄 Sending data back to Agent...\x1b[0m`);
|
|
221
|
-
await chat(`Action results:\n${agentFeedback}\nNext step? Output <file
|
|
255
|
+
await chat(`Action results:\n${agentFeedback}\nNext step? Output <file>, <notify>, or <execute> if ready, or <read>/<search>.`, depth + 1);
|
|
222
256
|
return;
|
|
223
257
|
}
|
|
224
258
|
|
|
@@ -231,9 +265,8 @@ async function chat(prompt, depth = 0) {
|
|
|
231
265
|
|
|
232
266
|
function startInteractiveMode() {
|
|
233
267
|
const config = loadConfig();
|
|
234
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
235
268
|
printLogo();
|
|
236
|
-
console.log(`\x1b[33mWelcome to NoxyAI Interactive
|
|
269
|
+
console.log(`\x1b[33mWelcome to NoxyAI Interactive OS!\x1b[0m`);
|
|
237
270
|
console.log(`Model: \x1b[32m${config.model}\x1b[0m | Agent Mode: ${config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m'}`);
|
|
238
271
|
console.log(`Commands: \x1b[36m/model\x1b[0m, \x1b[36m/agent\x1b[0m, \x1b[36m/clear\x1b[0m, \x1b[36m/exit\x1b[0m\n`);
|
|
239
272
|
|
|
@@ -271,7 +304,7 @@ function startInteractiveMode() {
|
|
|
271
304
|
}
|
|
272
305
|
|
|
273
306
|
if (command === 'login') login();
|
|
274
|
-
else if (command === 'logout') { const c = loadConfig(); c.token = null; saveConfig(c); console.log('Logged out.'); }
|
|
275
|
-
else if (command === 'help' || command === '--help') { printLogo(); console.log(` \x1b[32mnoxyai\x1b[0m Start Interactive Mode\n`); }
|
|
307
|
+
else if (command === 'logout') { const c = loadConfig(); c.token = null; saveConfig(c); console.log('Logged out.'); process.exit(0); }
|
|
308
|
+
else if (command === 'help' || command === '--help') { printLogo(); console.log(` \x1b[32mnoxyai\x1b[0m Start Interactive Mode\n`); process.exit(0); }
|
|
276
309
|
else if (!command) startInteractiveMode();
|
|
277
310
|
else { console.error(`❌ Unknown command.`); process.exit(1); }
|