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.
Files changed (2) hide show
  1. package/index-noxyai.js +53 -20
  2. 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
- process.stdout.write('\x1b[31m' + parsed.text + '\x1b[0m');
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
- // 🚨 EXPLICIT TOKEN BILLING READOUT
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
- const readRegex = /<read>([\s\S]*?)<\/read>/g; let match;
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
- searchData = JSON.parse(responseText);
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}. Tell the user the search tool is currently unavailable.]\n`;
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> or <execute> if ready, or <read>/<search>.`, depth + 1);
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 Mode!\x1b[0m`);
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); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-noxyai",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "CLI for NoxyAI",
5
5
  "main": "index-noxyai.js",
6
6
  "bin": {