npm-noxyai 1.0.9 → 1.0.11

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 CHANGED
@@ -12,6 +12,29 @@ const CONFIG_FILE = path.join(os.homedir(), '.noxyai.json');
12
12
  const args = process.argv.slice(2);
13
13
  const command = args[0];
14
14
 
15
+ // --- CONFIG MANAGEMENT ---
16
+ function loadConfig() {
17
+ if (fs.existsSync(CONFIG_FILE)) {
18
+ try {
19
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
20
+ if (config.agentMode === undefined) config.agentMode = true;
21
+ if (config.autoDeduct === undefined) config.autoDeduct = false;
22
+ return config;
23
+ } catch(e) {}
24
+ }
25
+ return {
26
+ token: null,
27
+ model: 'auto',
28
+ agentMode: true,
29
+ autoDeduct: false
30
+ };
31
+ }
32
+
33
+ function saveConfig(config) {
34
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
35
+ }
36
+
37
+ // --- UI HELPERS ---
15
38
  function printLogo() {
16
39
  console.log('\x1b[36m' + `
17
40
  ███╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗ █████╗ ██╗
@@ -23,40 +46,107 @@ function printLogo() {
23
46
  ` + '\x1b[0m');
24
47
  }
25
48
 
26
- function saveToken(token) {
27
- fs.writeFileSync(CONFIG_FILE, JSON.stringify({ token }));
49
+ function printFunnyArt() {
50
+ const art = `
51
+ ▓▓ ▓▓
52
+ ▓▓ ▓▓ ▓▓
53
+ ▓▓▓▓ ▓▓
54
+ ▓▓ ██ ▓▓
55
+ ▓▓░░░░ ▓▓
56
+ ▓▓▓▓░░ ▓▓
57
+ ▓▓ ▓▓
58
+ ▓▓ ▓▓ ▓▓
59
+ ▓▓ ▓▓ ▓▓ ▓▓
60
+ ▓▓ ▓▓ ▓▓ ▓▓
61
+ ▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░▓▓
62
+ ▓▓ ▓▓
63
+ ▓▓░░ ▓▓
64
+ ▓▓░░ ░░▓▓
65
+ ▓▓░░ ░░▓▓
66
+ ▓▓░░░░ ░░░░░░░░░░ ▓▓
67
+ ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓
68
+ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓
69
+ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓
70
+ ▓▓░░▓▓░░▓▓ ▓▓░░▓▓░░▓▓
71
+ ▓▓░░▓▓░░▓▓ ▓▓░░▓▓░░▓▓
72
+ ▓▓░░▓▓░░▓▓ ▓▓░░▓▓░░▓▓
73
+ ▓▓ ▓▓ ▓▓ ▓▓`;
74
+ console.log('\x1b[35m' + art + '\x1b[0m');
28
75
  }
29
76
 
30
- function getToken() {
31
- if (fs.existsSync(CONFIG_FILE)) {
32
- const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
33
- return config.token;
34
- }
35
- return null;
77
+ let spinnerInterval;
78
+ function startSpinner(text) {
79
+ const frames = ['⠋', '', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
80
+ let i = 0;
81
+ process.stdout.write('\x1b[?25l');
82
+ spinnerInterval = setInterval(() => {
83
+ process.stdout.write('\r\x1b[36m${frames[i]} ${text}\x1b[0m');
84
+ i = (i + 1) % frames.length;
85
+ }, 80);
86
+ }
87
+
88
+ function stopSpinner() {
89
+ clearInterval(spinnerInterval);
90
+ process.stdout.write('\r\x1b[K\x1b[?25h');
36
91
  }
37
92
 
38
93
  function logout() {
39
- if (fs.existsSync(CONFIG_FILE)) {
40
- fs.unlinkSync(CONFIG_FILE);
41
- console.log('✅ Successfully logged out.');
42
- } else {
43
- console.log('You are already logged out.');
44
- }
94
+ const config = loadConfig();
95
+ config.token = null;
96
+ saveConfig(config);
97
+ console.log('✅ Successfully logged out.');
45
98
  }
46
99
 
47
100
  function openBrowser(url) {
48
101
  const platform = os.platform();
49
- if (platform === 'android') {
50
- spawn('termux-open-url', [url], { stdio: 'ignore' });
51
- } else if (platform === 'darwin') {
52
- spawn('open', [url], { stdio: 'ignore' });
53
- } else if (platform === 'win32') {
54
- spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
55
- } else {
56
- spawn('xdg-open', [url], { stdio: 'ignore' });
102
+ if (platform === 'android') spawn('termux-open-url', [url], { stdio: 'ignore' });
103
+ else if (platform === 'darwin') spawn('open', [url], { stdio: 'ignore' });
104
+ else if (platform === 'win32') spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
105
+ else spawn('xdg-open', [url], { stdio: 'ignore' });
106
+ }
107
+
108
+ // --- SEARCH FUNCTION (Now uses backend) ---
109
+ async function searchWeb(query) {
110
+ try {
111
+ const config = loadConfig();
112
+ const res = await fetch('${BASE_URL}/api/io?action=search&q=' + encodeURIComponent(query), {
113
+ headers: { 'Authorization': 'Bearer ' + config.token }
114
+ });
115
+ const data = await res.json();
116
+ return data;
117
+ } catch (err) {
118
+ return { error: err.message };
57
119
  }
58
120
  }
59
121
 
122
+ // --- CREDIT DEDUCTION WITH MANUAL APPROVAL ---
123
+ async function requestCreditDeduction(usage, model) {
124
+ const config = loadConfig();
125
+
126
+ if (config.autoDeduct) {
127
+ return { approved: true, cost: usage.estimatedCost };
128
+ }
129
+
130
+ console.log('\n\x1b[33m💰 Credit Usage Summary:\x1b[0m');
131
+ console.log(' Prompt Tokens: ${usage.promptTokens}');
132
+ console.log(' Response Tokens: ${usage.responseTokens}');
133
+ if (usage.reasoningTokens) {
134
+ console.log(' \x1b[31mReasoning Tokens: ${usage.reasoningTokens}\x1b[0m');
135
+ }
136
+ console.log(' Total Tokens: ${usage.totalTokens}');
137
+ console.log(' \x1b[32mEstimated Cost: ${usage.estimatedCost} credits\x1b[0m');
138
+
139
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
140
+
141
+ return new Promise((resolve) => {
142
+ rl.question('\n\x1b[33mApprove credit deduction? (y/n): \x1b[0m', (answer) => {
143
+ rl.close();
144
+ resolve({ approved: answer.toLowerCase() === 'y', cost: usage.estimatedCost });
145
+ });
146
+ });
147
+ }
148
+
149
+ // --- CORE FUNCTIONS ---
60
150
  async function login() {
61
151
  printLogo();
62
152
  console.log('Initializing secure login...\n');
@@ -65,14 +155,14 @@ async function login() {
65
155
  const { deviceCode, verificationUrl, interval } = await initRes.json();
66
156
 
67
157
  console.log('=============================================');
68
- console.log(`URL: ${verificationUrl}`);
158
+ console.log('URL: ${verificationUrl}');
69
159
  console.log('=============================================\n');
70
160
 
71
161
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
72
162
  rl.question('Press ENTER to automatically open the browser...', () => {
73
163
  openBrowser(verificationUrl);
74
164
  rl.close();
75
- console.log('\n⏳ Waiting for authentication...');
165
+ startSpinner('Waiting for authentication...');
76
166
  });
77
167
 
78
168
  const pollInterval = setInterval(async () => {
@@ -87,7 +177,11 @@ async function login() {
87
177
 
88
178
  if (data.status === 'success') {
89
179
  clearInterval(pollInterval);
90
- saveToken(data.token);
180
+ stopSpinner();
181
+ const config = loadConfig();
182
+ config.token = data.token;
183
+ saveConfig(config);
184
+
91
185
  console.clear();
92
186
  printLogo();
93
187
  console.log('✅ Login successful! Terminal connected.');
@@ -95,6 +189,7 @@ async function login() {
95
189
  process.exit(0);
96
190
  } else if (data.error) {
97
191
  clearInterval(pollInterval);
192
+ stopSpinner();
98
193
  console.error('\n❌ Login failed: ' + data.error);
99
194
  process.exit(1);
100
195
  }
@@ -108,205 +203,313 @@ async function login() {
108
203
 
109
204
  function runTerminalCommand(cmd) {
110
205
  return new Promise((resolve, reject) => {
111
- console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
112
-
206
+ console.log('\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n');
113
207
  const isServer = cmd.includes('dev') || cmd.includes('serve') || cmd.includes('host') || cmd.includes('start');
114
-
115
- const child = spawn(cmd, {
116
- shell: true,
117
- stdio: 'inherit'
118
- });
208
+ const child = spawn(cmd, { shell: true, stdio: 'inherit' });
119
209
 
120
210
  if (isServer) {
121
- console.log(`\x1b[36m[i] Server detected. It will run in the foreground. Press Ctrl+C to stop it.\x1b[0m`);
211
+ console.log('\n\x1b[36m[i] Server detected. Running in foreground. Press Ctrl+C to stop.\x1b[0m');
122
212
  setTimeout(() => resolve(), 2500);
123
213
  }
124
214
 
125
215
  child.on('close', (code) => {
126
216
  if (!isServer) {
127
- if (code !== 0) reject(`Command failed with exit code ${code}`);
217
+ if (code !== 0) reject('Command failed with exit code ${code}');
128
218
  else resolve();
129
219
  }
130
220
  });
131
-
132
221
  child.on('error', (error) => reject(error.message));
133
222
  });
134
223
  }
135
224
 
136
225
  function getLocalContext() {
137
226
  try {
138
- const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 20);
139
- return `\n[SYSTEM CONTEXT: You are running inside the user's terminal. Current directory: ${process.cwd()}. Existing files here: ${files.join(', ')}. You can modify existing files or create new ones.]\n`;
140
- } catch (e) {
141
- return "";
142
- }
227
+ const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 30);
228
+ return '\n[SYSTEM: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n';
229
+ } catch (e) { return ""; }
143
230
  }
144
231
 
145
232
  async function chat(prompt, depth = 0) {
146
- const token = getToken();
147
- if (!token) {
148
- console.error('❌ Unauthorized: You must log in first. Run "noxyai login"');
149
- return;
150
- }
151
-
152
- if (depth > 3) {
153
- console.error('\n❌ Agent reached maximum retry depth. Please fix the error manually.');
154
- return;
155
- }
233
+ const config = loadConfig();
234
+ if (!config.token) { console.error('❌ Unauthorized: Run "noxyai login"'); return; }
235
+ if (depth > 6) { console.error('\nReached max reasoning depth. Stopping.'); return; }
156
236
 
157
237
  const enhancedPrompt = depth === 0 ? getLocalContext() + prompt : prompt;
158
- const model = 'auto';
238
+
239
+ startSpinner(depth === 0 ? 'NoxyAI is thinking...' : 'NoxyAI is analyzing results...');
159
240
 
160
241
  try {
161
242
  const res = await fetch(BASE_URL + '/api/io', {
162
243
  method: 'POST',
163
- headers: {
164
- 'Content-Type': 'application/json',
165
- 'Authorization': 'Bearer ' + token
166
- },
167
- body: JSON.stringify({ prompt: enhancedPrompt, model })
244
+ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
245
+ body: JSON.stringify({
246
+ prompt: enhancedPrompt,
247
+ model: config.model,
248
+ agentMode: config.agentMode
249
+ })
168
250
  });
169
251
 
252
+ stopSpinner();
253
+
170
254
  if (!res.ok) {
171
255
  const errorText = await res.text();
172
- console.error('\n❌ API Error: ' + errorText);
173
- return;
256
+ if (res.status === 402) {
257
+ console.log('\n\x1b[31m' + errorText + '\x1b[0m');
258
+ return;
259
+ }
260
+ return console.error('\n❌ API Error: ' + errorText);
174
261
  }
175
262
 
176
- if (depth === 0) console.log('\n🤖 \x1b[36mNoxyAI\x1b[0m is thinking...\n');
177
- else console.log('\n🤖 \x1b[36mNoxyAI\x1b[0m is analyzing the error and rewriting...\n');
263
+ const modelDisplay = config.model === 'auto' ? 'Llama 3.3 70B' : config.model;
264
+ const agentStatus = config.agentMode ? '🤖 Agent Mode' : '💬 Chat Mode';
265
+ console.log('\n${agentStatus} \x1b[36mNoxyAI (${modelDisplay}):\x1b[0m\n');
178
266
 
179
267
  const reader = res.body.getReader();
180
268
  const decoder = new TextDecoder('utf-8');
181
269
  let fullResponse = "";
270
+ let fullReasoning = "";
271
+ let usage = null;
272
+ let isQwen = false;
182
273
 
183
274
  while (true) {
184
275
  const { done, value } = await reader.read();
185
276
  if (done) break;
186
-
187
- const chunk = decoder.decode(value, { stream: true });
188
- const lines = chunk.split('\n');
189
-
277
+
278
+ const lines = decoder.decode(value, { stream: true }).split('\n');
190
279
  for (const line of lines) {
191
280
  if (line.startsWith('data: ')) {
192
281
  const data = line.slice(6).trim();
193
282
  if (data === '[DONE]') break;
283
+
194
284
  try {
195
285
  const parsed = JSON.parse(data);
286
+
287
+ if (parsed.isQwen) {
288
+ isQwen = true;
289
+ }
290
+
291
+ if (parsed.reasoning) {
292
+ fullReasoning += parsed.reasoning;
293
+ if (isQwen) {
294
+ process.stdout.write('\x1b[31m' + parsed.reasoning + '\x1b[0m');
295
+ }
296
+ }
297
+
196
298
  if (parsed.text) {
197
299
  fullResponse += parsed.text;
198
300
  process.stdout.write(parsed.text);
199
301
  }
302
+
303
+ if (parsed.usage) {
304
+ usage = parsed.usage;
305
+ }
200
306
  } catch (e) {}
201
307
  }
202
308
  }
203
309
  }
204
310
 
311
+ if (usage && depth === 0) {
312
+ const approval = await requestCreditDeduction(usage, config.model);
313
+
314
+ if (approval.approved) {
315
+ const deductRes = await fetch(BASE_URL + '/api/io/deduct', {
316
+ method: 'POST',
317
+ headers: {
318
+ 'Content-Type': 'application/json',
319
+ 'Authorization': 'Bearer ' + config.token
320
+ },
321
+ body: JSON.stringify({
322
+ amount: approval.cost,
323
+ model: config.model,
324
+ tokens: usage.totalTokens
325
+ })
326
+ });
327
+
328
+ if (deductRes.ok) {
329
+ console.log('\n\x1b[32m✅ Credits deducted successfully!\x1b[0m');
330
+ } else {
331
+ console.log('\n\x1b[31m❌ Credit deduction failed\x1b[0m');
332
+ }
333
+ } else {
334
+ console.log('\n\x1b[33m⚠️ Credit deduction cancelled\x1b[0m');
335
+ return;
336
+ }
337
+ }
338
+
339
+ if (!config.agentMode) {
340
+ console.log('\n');
341
+ return;
342
+ }
343
+
205
344
  console.log('\n\n\x1b[32m[Agent] Processing actions...\x1b[0m');
345
+ let agentFeedback = "";
206
346
 
207
- let filesCreated = 0;
208
- let commandsRun = 0;
347
+ const readRegex = /<read>([\s\S]*?)<\/read>/g;
348
+ let match;
349
+ while ((match = readRegex.exec(fullResponse)) !== null) {
350
+ const filePath = match[1].trim();
351
+ try {
352
+ const content = fs.readFileSync(filePath, 'utf8');
353
+ console.log('\x1b[34m📖 Read file:\x1b[0m ${filePath}');
354
+ agentFeedback += '\n[FILE: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n';
355
+ } catch (err) {
356
+ console.log('\x1b[31m❌ Failed to read:\x1b[0m ${filePath}');
357
+ agentFeedback += '\n[ERROR reading ${filePath}: ${err.message}]\n';
358
+ }
359
+ }
360
+
361
+ const searchRegex = /<search>([\s\S]*?)<\/search>/g;
362
+ while ((match = searchRegex.exec(fullResponse)) !== null) {
363
+ const query = match[1].trim();
364
+ console.log('\x1b[35m🔍 Searching Web:\x1b[0m ${query}');
365
+ try {
366
+ const searchData = await searchWeb(query);
367
+ const snippets = searchData.items ? searchData.items.map(i => '- ${i.title}: ${i.snippet}').join('\n') : "No results.";
368
+ agentFeedback += '\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n';
369
+ } catch (err) {
370
+ agentFeedback += '\n[SEARCH FAILED: ${err.message}]\n';
371
+ }
372
+ }
209
373
 
210
374
  const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
211
- let match;
212
375
  while ((match = fileRegex.exec(fullResponse)) !== null) {
213
- const filePath = match[1];
214
- const content = match[2];
215
-
376
+ const filePath = match[1], content = match[2];
216
377
  const dir = path.dirname(filePath);
217
378
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
218
379
  fs.writeFileSync(filePath, content.trim());
219
- console.log(`\x1b[32m✔ Created/Modified file:\x1b[0m ${filePath}`);
220
- filesCreated++;
380
+ console.log('\x1b[32m✔ Wrote file:\x1b[0m ${filePath}');
221
381
  }
222
382
 
223
383
  const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
224
384
  const commands = [];
225
- while ((match = execRegex.exec(fullResponse)) !== null) {
226
- commands.push(match[1].trim());
227
- }
228
-
385
+ while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
386
+
229
387
  for (const cmd of commands) {
230
- commandsRun++;
231
388
  try {
232
389
  await runTerminalCommand(cmd);
233
390
  } catch (err) {
234
- console.error(`\n\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
235
- console.log(`\x1b[33m🔄 Triggering Auto-Heal Loop...\x1b[0m`);
236
- const errorPrompt = `I ran the command "${cmd}" and got this error:\n\n${err}\n\nPlease fix the issue. Use <file> tags to rewrite files or <execute> to run commands.`;
237
- await chat(errorPrompt, depth + 1);
238
- return;
391
+ console.log('\x1b[31m❌ Command Failed:\x1b[0m ${err}');
392
+ agentFeedback += '\n[COMMAND ERROR running "${cmd}": ${err}]\n';
239
393
  }
240
394
  }
241
395
 
396
+ if (agentFeedback) {
397
+ console.log('\n\x1b[33m🔄 Sending data back to Agent...\x1b[0m');
398
+ await chat('Here are the results of your actions:\n${agentFeedback}\nWhat is the next step? Output <file> or <execute> if ready, or <read>/<search> if you need more info.', depth + 1);
399
+ return;
400
+ }
401
+
242
402
  console.log('\n\x1b[32m════════════════════════════════════════\x1b[0m');
243
- console.log(`\x1b[32m✨ Task Completed Successfully!\x1b[0m`);
244
- if (filesCreated > 0) console.log(`📄 Modified ${filesCreated} file(s)`);
245
- if (commandsRun > 0) console.log(`🚀 Executed ${commandsRun} command(s)`);
246
- if (filesCreated === 0 && commandsRun === 0) console.log(`💬 Answered query`);
247
- console.log('\x1b[32m════════════════════════════════════════\x1b[0m\n');
403
+ console.log('\x1b[32m✨ Task Completed!\x1b[0m');
404
+ console.log('\n\x1b[32m════════════════════════════════════════\x1b[0m\n');
248
405
 
249
- } catch (error) {
250
- console.error('\n❌ Connection error: ' + error.message);
406
+ } catch (error) {
407
+ stopSpinner();
408
+ console.error('\n❌ Connection error: ' + error.message);
251
409
  }
252
410
  }
253
411
 
254
- function showHelp() {
255
- printLogo();
256
- console.log(`
257
- \x1b[33mNoxyAI Agent CLI - Available Commands\x1b[0m
258
-
259
- \x1b[32mnoxyai\x1b[0m Start Interactive Mode (Recommended)
260
- \x1b[32mnoxyai login\x1b[0m Authenticate your terminal
261
- \x1b[32mnoxyai logout\x1b[0m Remove authentication from this device
262
- \x1b[32mnoxyai chat "<prompt>"\x1b[0m Run a single prompt and exit
263
- \x1b[32mnoxyai help\x1b[0m Show this menu
264
- `);
265
- }
266
-
267
412
  function startInteractiveMode() {
413
+ const config = loadConfig();
268
414
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
269
415
  printLogo();
270
- console.log(`\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m`);
271
- console.log(`Type your prompt and press Enter. Type 'exit' to quit.\n`);
416
+ printFunnyArt();
417
+
418
+ const agentStatus = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
419
+ const deductStatus = config.autoDeduct ? '\x1b[32mAUTO\x1b[0m' : '\x1b[33mMANUAL\x1b[0m';
420
+
421
+ console.log('\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m');
422
+ console.log('Current Model: \x1b[32m${config.model}\x1b[0m');
423
+ console.log('Agent Mode: ${agentStatus} | Credit Mode: ${deductStatus}');
424
+ console.log('\nCommands:');
425
+ console.log(' \x1b[36m/agent\x1b[0m - Toggle Agent Mode (3-model collaboration)');
426
+ console.log(' \x1b[36m/auto\x1b[0m - Toggle Auto Credit Deduction');
427
+ console.log(' \x1b[36m/model\x1b[0m - Change AI Model');
428
+ console.log(' \x1b[36m/clear\x1b[0m - Clear screen');
429
+ console.log(' \x1b[36m/exit\x1b[0m - Exit\n');
272
430
 
273
431
  function ask() {
274
- rl.question('\x1b[36mNoxyAI > \x1b[0m', async (input) => {
432
+ const prompt = config.agentMode ? '\x1b[36mNoxyAI [Agent] > \x1b[0m' : '\x1b[36mNoxyAI [Chat] > \x1b[0m';
433
+ rl.question(prompt, async (input) => {
275
434
  const trimmed = input.trim();
276
- if (trimmed.toLowerCase() === 'exit' || trimmed.toLowerCase() === 'quit') {
277
- console.log('Goodbye!');
435
+ const lower = trimmed.toLowerCase();
436
+
437
+ if (lower === 'exit' || lower === '/exit') {
438
+ console.log('\n\x1b[36mGoodbye! 👋\x1b[0m\n');
278
439
  process.exit(0);
440
+ }
441
+ else if (lower === '/clear') {
442
+ console.clear();
443
+ printLogo();
444
+ printFunnyArt();
445
+ ask();
446
+ }
447
+ else if (lower === '/agent') {
448
+ config.agentMode = !config.agentMode;
449
+ saveConfig(config);
450
+ const status = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
451
+ console.log('\n\x1b[33m🤖 Agent Mode: ${status}\x1b[0m');
452
+ if (config.agentMode) {
453
+ console.log('\n\x1b[36mAgent mode uses 3 models working together:\x1b[0m');
454
+ console.log(' • Search & Analysis');
455
+ console.log(' • Reasoning & Planning');
456
+ console.log(' • Code Generation & Execution\n');
457
+ }
458
+ ask();
459
+ }
460
+ else if (lower === '/auto') {
461
+ config.autoDeduct = !config.autoDeduct;
462
+ saveConfig(config);
463
+ const status = config.autoDeduct ? '\x1b[32mAUTO\x1b[0m' : '\x1b[33mMANUAL\x1b[0m';
464
+ console.log('\n\x1b[33m💰 Credit Deduction Mode: ${status}\x1b[0m\n');
465
+ ask();
279
466
  }
280
- if (trimmed) {
467
+ else if (lower === '/model') {
468
+ console.log('\n\x1b[33mSelect an AI Model:\x1b[0m');
469
+ console.log(' \x1b[36m1)\x1b[0m Auto (Llama 3.3 70B - Fast/Coding)');
470
+ console.log(' \x1b[36m2)\x1b[0m Qwen3 Next 80B Thinking (Deep Reasoning) \x1b[31m[Reasoning in Red]\x1b[0m');
471
+ console.log(' \x1b[36m3)\x1b[0m GLM 4.7');
472
+
473
+ rl.question('\nEnter number (1-3): ', (choice) => {
474
+ let selected = 'auto';
475
+ if (choice === '2') selected = 'Qwen3';
476
+ if (choice === '3') selected = 'GLM';
477
+
478
+ config.model = selected;
479
+ saveConfig(config);
480
+
481
+ console.log('\n\x1b[32m✔ Model successfully changed to: ${selected}\x1b[0m\n');
482
+ ask();
483
+ });
484
+ return;
485
+ }
486
+ else if (trimmed) {
281
487
  await chat(trimmed);
488
+ ask();
489
+ } else {
490
+ ask();
282
491
  }
283
- ask();
284
492
  });
285
493
  }
286
494
  ask();
287
495
  }
288
496
 
289
- // --- ROUTER ---
290
- if (command === 'login') {
291
- login();
292
- } else if (command === 'logout') {
293
- logout();
294
- process.exit(0);
295
- } else if (command === 'help' || command === '--help') { // 🚨 BUG FIXED HERE!
296
- showHelp();
297
- process.exit(0);
298
- } else if (command === 'chat') {
299
- const prompt = args.slice(1).join(' ');
300
- if (!prompt) {
301
- console.error(' Please provide a prompt. Example: noxyai chat "Create a python script"');
302
- process.exit(1);
303
- }
304
- chat(prompt).then(() => process.exit(0));
305
- } else if (!command) {
306
- // Now this actually triggers!
307
- startInteractiveMode();
308
- } else {
309
- console.error(`❌ Unknown command: ${command}`);
310
- console.log('Run "noxyai help" for a list of commands.');
311
- process.exit(1);
497
+ if (command === 'login') login();
498
+ else if (command === 'logout') logout();
499
+ else if (command === 'help' || command === '--help') {
500
+ printLogo();
501
+ console.log('\n \x1b[32mnoxyai\x1b[0m Start Interactive Mode');
502
+ console.log(' \x1b[32mnoxyai chat "<prompt>"\x1b[0m Run single prompt');
503
+ console.log('\n Interactive Commands:');
504
+ console.log(' \x1b[36m/agent\x1b[0m Toggle Agent Mode (3-model collaboration)');
505
+ console.log(' \x1b[36m/auto\x1b[0m Toggle Auto Credit Deduction');
506
+ console.log(' \x1b[36m/model\x1b[0m Change AI Model\n');
507
+ }
508
+ else if (command === 'chat') {
509
+ chat(args.slice(1).join(' ')).then(() => process.exit(0));
510
+ }
511
+ else if (!command) startInteractiveMode();
512
+ else {
513
+ console.error('❌ Unknown command. Run "noxyai help"');
514
+ process.exit(1);
312
515
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-noxyai",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "CLI for NoxyAI",
5
5
  "main": "index-noxyai.js",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  "keywords": [],
16
16
  "type": "commonjs",
17
17
  "dependencies": {
18
- "express": "^5.2.1"
18
+ "express": "^5.2.1",
19
+ "http": "^0.0.1-security"
19
20
  }
20
21
  }
package/index.html DELETED
@@ -1,13 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Snake Game</title>
7
- <link rel="stylesheet" href="style.css">
8
- </head>
9
- <body>
10
- <canvas id="gameCanvas" width="400" height="400"></canvas>
11
- <script src="script.js"></script>
12
- </body>
13
- </html>
package/reinstall_pip.py DELETED
@@ -1 +0,0 @@
1
- import pip; pip.uninstall('pip'); import os; os.system('python -m ensurepip')
package/script.js DELETED
@@ -1,92 +0,0 @@
1
- const canvas = document.getElementById('gameCanvas');
2
- const ctx = canvas.getContext('2d');
3
-
4
- let snake = [
5
- {x: 200, y: 200},
6
- {x: 190, y: 200},
7
- {x: 180, y: 200},
8
- {x: 170, y: 200},
9
- {x: 160, y: 200}
10
- ];
11
-
12
- let direction = 'right';
13
- let score = 0;
14
- let food = {x: Math.floor(Math.random() * 40) * 10, y: Math.floor(Math.random() * 40) * 10};
15
-
16
- function draw() {
17
- ctx.clearRect(0, 0, canvas.width, canvas.height);
18
- for (let i = 0; i < snake.length; i++) {
19
- ctx.fillStyle = 'green';
20
- ctx.fillRect(snake[i].x, snake[i].y, 10, 10);
21
- }
22
- ctx.fillStyle = 'red';
23
- ctx.fillRect(food.x, food.y, 10, 10);
24
- ctx.fillStyle = 'black';
25
- ctx.font = '24px Arial';
26
- ctx.textAlign = 'left';
27
- ctx.textBaseline = 'top';
28
- ctx.fillText('Score: ' + score, 10, 10);
29
- }
30
-
31
- function update() {
32
- for (let i = snake.length - 1; i > 0; i--) {
33
- snake[i] = {x: snake[i - 1].x, y: snake[i - 1].y};
34
- }
35
- if (direction === 'right') {
36
- snake[0].x += 10;
37
- } else if (direction === 'left') {
38
- snake[0].x -= 10;
39
- } else if (direction === 'up') {
40
- snake[0].y -= 10;
41
- } else if (direction === 'down') {
42
- snake[0].y += 10;
43
- }
44
- if (snake[0].x === food.x && snake[0].y === food.y) {
45
- score++;
46
- food = {x: Math.floor(Math.random() * 40) * 10, y: Math.floor(Math.random() * 40) * 10};
47
- snake.push({x: snake[snake.length - 1].x, y: snake[snake.length - 1].y});
48
- }
49
- for (let i = 1; i < snake.length; i++) {
50
- if (snake[0].x === snake[i].x && snake[0].y === snake[i].y) {
51
- alert('Game Over');
52
- score = 0;
53
- snake = [
54
- {x: 200, y: 200},
55
- {x: 190, y: 200},
56
- {x: 180, y: 200},
57
- {x: 170, y: 200},
58
- {x: 160, y: 200}
59
- ];
60
- direction = 'right';
61
- }
62
- }
63
- if (snake[0].x < 0 || snake[0].x >= canvas.width || snake[0].y < 0 || snake[0].y >= canvas.height) {
64
- alert('Game Over');
65
- score = 0;
66
- snake = [
67
- {x: 200, y: 200},
68
- {x: 190, y: 200},
69
- {x: 180, y: 200},
70
- {x: 170, y: 200},
71
- {x: 160, y: 200}
72
- ];
73
- direction = 'right';
74
- }
75
- }
76
-
77
- setInterval(() => {
78
- update();
79
- draw();
80
- }, 100);
81
-
82
- document.addEventListener('keydown', (e) => {
83
- if (e.key === 'ArrowRight' && direction !== 'left') {
84
- direction = 'right';
85
- } else if (e.key === 'ArrowLeft' && direction !== 'right') {
86
- direction = 'left';
87
- } else if (e.key === 'ArrowUp' && direction !== 'down') {
88
- direction = 'up';
89
- } else if (e.key === 'ArrowDown' && direction !== 'up') {
90
- direction = 'down';
91
- }
92
- });
package/server.js DELETED
@@ -1,11 +0,0 @@
1
- const express = require('express');
2
- const app = express();
3
- const port = 3000;
4
-
5
- app.get('/', (req, res) => {
6
- res.send('Hello World!');
7
- });
8
-
9
- app.listen(port, () => {
10
- console.log(`Server running on port ${port}`);
11
- });
package/snake_game.py DELETED
@@ -1,90 +0,0 @@
1
- import pygame
2
- import sys
3
- import time
4
- import random
5
-
6
- # Direction Constants
7
- UP = 1
8
- RIGHT = 2
9
- DOWN = 3
10
- LEFT = 4
11
-
12
- class SnakeGame:
13
- def __init__(self, width=800, height=600):
14
- self.width = width
15
- self.height = height
16
- self.snake = [(200, 200), (220, 200), (240, 200)]
17
- self.direction = RIGHT
18
- self.apple = self.set_new_apple()
19
- self.score = 0
20
- pygame.init()
21
- self.display = pygame.display.set_mode((width, height))
22
- self.font = pygame.font.Font(None, 36)
23
-
24
- def set_new_apple(self):
25
- while True:
26
- x = random.randint(0, self.width - 20) // 20 * 20
27
- y = random.randint(0, self.height - 20) // 20 * 20
28
- apple = (x, y)
29
- if apple not in self.snake:
30
- return apple
31
-
32
- def play(self):
33
- clock = pygame.time.Clock()
34
- while True:
35
- for event in pygame.event.get():
36
- if event.type == pygame.QUIT:
37
- pygame.quit()
38
- sys.exit()
39
- elif event.type == pygame.KEYDOWN:
40
- if event.key == pygame.K_UP and self.direction != DOWN:
41
- self.direction = UP
42
- elif event.key == pygame.K_DOWN and self.direction != UP:
43
- self.direction = DOWN
44
- elif event.key == pygame.K_LEFT and self.direction != RIGHT:
45
- self.direction = LEFT
46
- elif event.key == pygame.K_RIGHT and self.direction != LEFT:
47
- self.direction = RIGHT
48
-
49
- head = self.snake[-1]
50
- if self.direction == UP:
51
- new_head = (head[0], head[1] - 20)
52
- elif self.direction == DOWN:
53
- new_head = (head[0], head[1] + 20)
54
- elif self.direction == LEFT:
55
- new_head = (head[0] - 20, head[1])
56
- elif self.direction == RIGHT:
57
- new_head = (head[0] + 20, head[1])
58
-
59
- self.snake.append(new_head)
60
- if self.apple == new_head:
61
- self.apple = self.set_new_apple()
62
- self.score += 1
63
- else:
64
- self.snake.pop(0)
65
-
66
- if (new_head[0] < 0 or new_head[0] >= self.width or
67
- new_head[1] < 0 or new_head[1] >= self.height or
68
- new_head in self.snake[:-1]):
69
- break
70
-
71
- self.display.fill((0, 0, 0))
72
- for pos in self.snake:
73
- pygame.draw.rect(self.display, (0, 255, 0), (pos[0], pos[1], 20, 20))
74
- pygame.draw.rect(self.display, (255, 0, 0), (self.apple[0], self.apple[1], 20, 20))
75
- text = self.font.render(f'Score: {self.score}', True, (255, 255, 255))
76
- self.display.blit(text, (10, 10))
77
- pygame.display.flip()
78
- clock.tick(10)
79
-
80
- time.sleep(1)
81
- self.display.fill((0, 0, 0))
82
- text = self.font.render('Game Over', True, (255, 255, 255))
83
- self.display.blit(text, (self.width // 2 - 50, self.height // 2 - 18))
84
- pygame.display.flip()
85
- time.sleep(2)
86
- pygame.quit()
87
-
88
- if __name__ == '__main__':
89
- game = SnakeGame()
90
- game.play()
package/style.css DELETED
@@ -1,11 +0,0 @@
1
- body {
2
- display: flex;
3
- justify-content: center;
4
- align-items: center;
5
- height: 100vh;
6
- background-color: #f0f0f0;
7
- }
8
-
9
- #gameCanvas {
10
- border: 1px solid black;
11
- }
package/tic_tac_toe.py DELETED
@@ -1,111 +0,0 @@
1
- import random
2
-
3
- class TicTacToe:
4
- def __init__(self):
5
- self.board = [' ' for _ in range(9)]
6
-
7
- def print_board(self):
8
- row1 = '| {} | {} | {} |'.format(self.board[0], self.board[1], self.board[2])
9
- row2 = '| {} | {} | {} |'.format(self.board[3], self.board[4], self.board[5])
10
- row3 = '| {} | {} | {} |'.format(self.board[6], self.board[7], self.board[8])
11
-
12
- print()
13
- print(row1)
14
- print(row2)
15
- print(row3)
16
- print()
17
-
18
- def available_moves(self):
19
- return [i for i, spot in enumerate(self.board) if spot == ' ']
20
-
21
- def empty_cells(self):
22
- return ' ' in self.board
23
-
24
- def num_empty_cells(self):
25
- return self.board.count(' ')
26
-
27
- def make_move(self, letter, move):
28
- self.board[move] = letter
29
-
30
- def winner(self):
31
- winning_combos = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
32
- for combo in winning_combos:
33
- if self.board[combo[0]] == self.board[combo[1]] == self.board[combo[2]] != ' ':
34
- return self.board[combo[0]]
35
- if ' ' not in self.board:
36
- return 'Tie'
37
- return False
38
-
39
- def minimax(board, depth, is_maximizing):
40
- result = board.winner()
41
- if result:
42
- if result == 'X':
43
- return -10 + depth
44
- elif result == 'O':
45
- return 10 - depth
46
- elif result == 'Tie':
47
- return 0
48
-
49
- if is_maximizing:
50
- best_score = float('-inf')
51
- for move in board.available_moves():
52
- board.make_move('O', move)
53
- score = minimax(board, depth + 1, False)
54
- board.board[move] = ' '
55
- best_score = max(score, best_score)
56
- return best_score
57
- else:
58
- best_score = float('inf')
59
- for move in board.available_moves():
60
- board.make_move('X', move)
61
- score = minimax(board, depth + 1, True)
62
- board.board[move] = ' '
63
- best_score = min(score, best_score)
64
- return best_score
65
-
66
- def ai_move(board):
67
- best_score = float('-inf')
68
- best_move = 0
69
- for move in board.available_moves():
70
- board.make_move('O', move)
71
- score = minimax(board, 0, False)
72
- board.board[move] = ' '
73
- if score > best_score:
74
- best_score = score
75
- best_move = move
76
- return best_move
77
-
78
- def main():
79
- board = TicTacToe()
80
- while True:
81
- board.print_board()
82
- move = input("Enter your move (1-9): ")
83
- if board.board[int(move) - 1] != ' ':
84
- print("Invalid move, try again.")
85
- continue
86
- board.make_move('X', int(move) - 1)
87
- result = board.winner()
88
- if result:
89
- board.print_board()
90
- if result == 'X':
91
- print("You win!")
92
- elif result == 'O':
93
- print("AI wins!")
94
- else:
95
- print("It's a tie!")
96
- break
97
- move = ai_move(board)
98
- board.make_move('O', move)
99
- result = board.winner()
100
- if result:
101
- board.print_board()
102
- if result == 'X':
103
- print("You win!")
104
- elif result == 'O':
105
- print("AI wins!")
106
- else:
107
- print("It's a tie!")
108
- break
109
-
110
- if __name__ == '__main__':
111
- main()