npm-noxyai 1.0.14 → 1.0.16
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 +89 -259
- package/package.json +1 -1
package/index-noxyai.js
CHANGED
|
@@ -12,27 +12,14 @@ 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
15
|
function loadConfig() {
|
|
17
16
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
18
|
-
try {
|
|
19
|
-
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
20
|
-
if (config.agentMode === undefined) config.agentMode = false; // Default OFF
|
|
21
|
-
return config;
|
|
22
|
-
} catch(e) {}
|
|
17
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch(e){}
|
|
23
18
|
}
|
|
24
|
-
return {
|
|
25
|
-
token: null,
|
|
26
|
-
model: 'auto',
|
|
27
|
-
agentMode: false // Default to Chat mode
|
|
28
|
-
};
|
|
19
|
+
return { token: null, model: 'auto', agentMode: false };
|
|
29
20
|
}
|
|
21
|
+
function saveConfig(config) { fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); }
|
|
30
22
|
|
|
31
|
-
function saveConfig(config) {
|
|
32
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// --- UI HELPERS ---
|
|
36
23
|
function printLogo() {
|
|
37
24
|
console.log('\x1b[36m' + `
|
|
38
25
|
███╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗ █████╗ ██╗
|
|
@@ -48,100 +35,48 @@ let spinnerInterval;
|
|
|
48
35
|
function startSpinner(text) {
|
|
49
36
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
50
37
|
let i = 0;
|
|
51
|
-
process.stdout.write('\x1b[?25l');
|
|
38
|
+
process.stdout.write('\x1b[?25l');
|
|
52
39
|
spinnerInterval = setInterval(() => {
|
|
53
40
|
process.stdout.write(`\r\x1b[36m${frames[i]} ${text}\x1b[0m`);
|
|
54
41
|
i = (i + 1) % frames.length;
|
|
55
42
|
}, 80);
|
|
56
43
|
}
|
|
57
|
-
|
|
58
44
|
function stopSpinner() {
|
|
59
45
|
clearInterval(spinnerInterval);
|
|
60
46
|
process.stdout.write('\r\x1b[K\x1b[?25h');
|
|
61
47
|
}
|
|
62
48
|
|
|
63
|
-
function logout() {
|
|
64
|
-
const config = loadConfig();
|
|
65
|
-
config.token = null;
|
|
66
|
-
saveConfig(config);
|
|
67
|
-
console.log('✅ Successfully logged out.');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function openBrowser(url) {
|
|
71
|
-
const platform = os.platform();
|
|
72
|
-
if (platform === 'android') spawn('termux-open-url', [url], { stdio: 'ignore' });
|
|
73
|
-
else if (platform === 'darwin') spawn('open', [url], { stdio: 'ignore' });
|
|
74
|
-
else if (platform === 'win32') spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
|
|
75
|
-
else spawn('xdg-open', [url], { stdio: 'ignore' });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// --- SEARCH FUNCTION ---
|
|
79
|
-
async function searchWeb(query) {
|
|
80
|
-
try {
|
|
81
|
-
const config = loadConfig();
|
|
82
|
-
const res = await fetch(`${BASE_URL}/api/io?action=search&q=${encodeURIComponent(query)}`, {
|
|
83
|
-
headers: { 'Authorization': 'Bearer ' + config.token }
|
|
84
|
-
});
|
|
85
|
-
const data = await res.json();
|
|
86
|
-
return data;
|
|
87
|
-
} catch (err) {
|
|
88
|
-
return { error: err.message };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// --- CORE FUNCTIONS ---
|
|
93
49
|
async function login() {
|
|
94
50
|
printLogo();
|
|
95
51
|
console.log('Initializing secure login...\n');
|
|
96
52
|
try {
|
|
97
53
|
const initRes = await fetch(BASE_URL + '/api/cli/init', { method: 'POST' });
|
|
98
54
|
const { deviceCode, verificationUrl, interval } = await initRes.json();
|
|
99
|
-
|
|
100
|
-
console.log('=============================================');
|
|
101
|
-
console.log(`URL: ${verificationUrl}`);
|
|
102
|
-
console.log('=============================================\n');
|
|
55
|
+
console.log(`URL: ${verificationUrl}\n`);
|
|
103
56
|
|
|
104
57
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
105
58
|
rl.question('Press ENTER to automatically open the browser...', () => {
|
|
106
|
-
|
|
59
|
+
const platform = os.platform();
|
|
60
|
+
if (platform === 'android') spawn('termux-open-url', [verificationUrl], { stdio: 'ignore' });
|
|
61
|
+
else spawn('xdg-open', [verificationUrl], { stdio: 'ignore' });
|
|
107
62
|
rl.close();
|
|
108
63
|
startSpinner('Waiting for authentication...');
|
|
109
64
|
});
|
|
110
65
|
|
|
111
66
|
const pollInterval = setInterval(async () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
stopSpinner();
|
|
124
|
-
const config = loadConfig();
|
|
125
|
-
config.token = data.token;
|
|
126
|
-
saveConfig(config);
|
|
127
|
-
|
|
128
|
-
console.clear();
|
|
129
|
-
printLogo();
|
|
130
|
-
console.log('✅ Login successful! Terminal connected.');
|
|
131
|
-
console.log('Type "noxyai" to start interactive mode.\n');
|
|
132
|
-
process.exit(0);
|
|
133
|
-
} else if (data.error) {
|
|
134
|
-
clearInterval(pollInterval);
|
|
135
|
-
stopSpinner();
|
|
136
|
-
console.error('\n❌ Login failed: ' + data.error);
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
} catch (e) {}
|
|
67
|
+
const pollRes = await fetch(BASE_URL + '/api/cli/poll', { method: 'POST', body: JSON.stringify({ deviceCode }) });
|
|
68
|
+
const data = await pollRes.json();
|
|
69
|
+
if (data.status === 'success') {
|
|
70
|
+
clearInterval(pollInterval); stopSpinner();
|
|
71
|
+
const config = loadConfig(); config.token = data.token; saveConfig(config);
|
|
72
|
+
console.clear(); printLogo();
|
|
73
|
+
console.log('✅ Login successful! Type "noxyai" to start.\n'); process.exit(0);
|
|
74
|
+
} else if (data.error) {
|
|
75
|
+
clearInterval(pollInterval); stopSpinner();
|
|
76
|
+
console.error('\n❌ Login failed: ' + data.error); process.exit(1);
|
|
77
|
+
}
|
|
140
78
|
}, interval * 1000);
|
|
141
|
-
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error('Failed to connect to NoxyAI server.', error.message);
|
|
144
|
-
}
|
|
79
|
+
} catch (error) { console.error('Failed to connect.', error.message); }
|
|
145
80
|
}
|
|
146
81
|
|
|
147
82
|
function runTerminalCommand(cmd) {
|
|
@@ -149,18 +84,11 @@ function runTerminalCommand(cmd) {
|
|
|
149
84
|
console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
|
|
150
85
|
const isServer = cmd.includes('dev') || cmd.includes('serve') || cmd.includes('host') || cmd.includes('start');
|
|
151
86
|
const child = spawn(cmd, { shell: true, stdio: 'inherit' });
|
|
152
|
-
|
|
153
87
|
if (isServer) {
|
|
154
|
-
console.log(`\
|
|
88
|
+
console.log(`\x1b[36m[i] Server detected. Running in foreground. Press Ctrl+C to stop.\x1b[0m`);
|
|
155
89
|
setTimeout(() => resolve(), 2500);
|
|
156
90
|
}
|
|
157
|
-
|
|
158
|
-
child.on('close', (code) => {
|
|
159
|
-
if (!isServer) {
|
|
160
|
-
if (code !== 0) reject(`Command failed with exit code ${code}`);
|
|
161
|
-
else resolve();
|
|
162
|
-
}
|
|
163
|
-
});
|
|
91
|
+
child.on('close', (code) => { if (!isServer) { if (code !== 0) reject(`Exit code ${code}`); else resolve(); } });
|
|
164
92
|
child.on('error', (error) => reject(error.message));
|
|
165
93
|
});
|
|
166
94
|
}
|
|
@@ -168,147 +96,109 @@ function runTerminalCommand(cmd) {
|
|
|
168
96
|
function getLocalContext() {
|
|
169
97
|
try {
|
|
170
98
|
const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 30);
|
|
171
|
-
return `\n[SYSTEM: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n`;
|
|
99
|
+
return `\n[SYSTEM CONTEXT: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n`;
|
|
172
100
|
} catch (e) { return ""; }
|
|
173
101
|
}
|
|
174
102
|
|
|
175
103
|
async function chat(prompt, depth = 0) {
|
|
176
104
|
const config = loadConfig();
|
|
177
105
|
if (!config.token) { console.error('❌ Unauthorized: Run "noxyai login"'); return; }
|
|
178
|
-
if (depth >
|
|
106
|
+
if (depth > 5) { console.error('\n❌ Reached max reasoning depth. Stopping to prevent loop.'); return; }
|
|
179
107
|
|
|
180
|
-
// Only add file context in Agent Mode
|
|
181
108
|
const enhancedPrompt = (depth === 0 && config.agentMode) ? getLocalContext() + prompt : prompt;
|
|
182
|
-
|
|
183
109
|
startSpinner(depth === 0 ? 'NoxyAI is thinking...' : 'NoxyAI is analyzing results...');
|
|
184
110
|
|
|
185
111
|
try {
|
|
186
112
|
const res = await fetch(BASE_URL + '/api/io', {
|
|
187
113
|
method: 'POST',
|
|
188
114
|
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
|
|
189
|
-
body: JSON.stringify({
|
|
190
|
-
prompt: enhancedPrompt,
|
|
191
|
-
model: config.model,
|
|
192
|
-
agentMode: config.agentMode
|
|
193
|
-
})
|
|
115
|
+
body: JSON.stringify({ prompt: enhancedPrompt, model: config.model, agentMode: config.agentMode })
|
|
194
116
|
});
|
|
195
117
|
|
|
196
118
|
stopSpinner();
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const errorText = await res.text();
|
|
200
|
-
if (res.status === 402) {
|
|
201
|
-
console.log('\n\x1b[31m' + errorText + '\x1b[0m');
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
return console.error('\n❌ API Error: ' + errorText);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const modelDisplay = config.model === 'auto' ? 'Mamba-Codestral 7B' :
|
|
208
|
-
config.model === 'Qwen3' ? 'Qwen3 80B' : 'Mamba-Codestral 7B';
|
|
209
|
-
const agentStatus = config.agentMode ? '🤖 Agent' : '💬 Chat';
|
|
210
|
-
console.log(`\n${agentStatus} \x1b[36mNoxyAI (${modelDisplay}):\x1b[0m\n`);
|
|
119
|
+
if (!res.ok) return console.error('\n❌ API Error: ' + await res.text());
|
|
120
|
+
console.log(`\n🤖 \x1b[36mNoxyAI (${config.model}):\x1b[0m\n`);
|
|
211
121
|
|
|
212
122
|
const reader = res.body.getReader();
|
|
213
123
|
const decoder = new TextDecoder('utf-8');
|
|
214
124
|
let fullResponse = "";
|
|
215
|
-
let usage = null;
|
|
216
|
-
let searchSources = [];
|
|
217
125
|
|
|
218
126
|
while (true) {
|
|
219
127
|
const { done, value } = await reader.read();
|
|
220
128
|
if (done) break;
|
|
221
|
-
|
|
222
129
|
const lines = decoder.decode(value, { stream: true }).split('\n');
|
|
223
130
|
for (const line of lines) {
|
|
224
131
|
if (line.startsWith('data: ')) {
|
|
225
132
|
const data = line.slice(6).trim();
|
|
226
133
|
if (data === '[DONE]') break;
|
|
227
|
-
|
|
228
134
|
try {
|
|
229
135
|
const parsed = JSON.parse(data);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (parsed.
|
|
233
|
-
process.stdout.write('\x1b[32m' + parsed.reasoning + '\x1b[0m');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Normal response
|
|
237
|
-
if (parsed.text) {
|
|
136
|
+
if (parsed.isReasoning) {
|
|
137
|
+
process.stdout.write('\x1b[31m' + parsed.text + '\x1b[0m');
|
|
138
|
+
} else if (parsed.text) {
|
|
238
139
|
fullResponse += parsed.text;
|
|
239
140
|
process.stdout.write(parsed.text);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
usage = parsed.usage;
|
|
141
|
+
} 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`);
|
|
244
144
|
}
|
|
245
145
|
} catch (e) {}
|
|
246
146
|
}
|
|
247
147
|
}
|
|
248
148
|
}
|
|
249
149
|
|
|
250
|
-
console.log(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (!config.agentMode) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Check if response contains action tags before processing
|
|
258
|
-
const hasActionTags = /<read>|<search>|<file path=|<execute>/.test(fullResponse);
|
|
259
|
-
if (!hasActionTags) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
console.log('\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
150
|
+
if (!config.agentMode) { console.log(); return; }
|
|
151
|
+
|
|
152
|
+
console.log('\n\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
264
153
|
let agentFeedback = "";
|
|
265
154
|
|
|
266
|
-
|
|
267
|
-
const readRegex = /<read>([\s\S]*?)<\/read>/g;
|
|
268
|
-
let match;
|
|
155
|
+
const readRegex = /<read>([\s\S]*?)<\/read>/g; let match;
|
|
269
156
|
while ((match = readRegex.exec(fullResponse)) !== null) {
|
|
270
157
|
const filePath = match[1].trim();
|
|
271
158
|
try {
|
|
272
159
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
273
160
|
console.log(`\x1b[34m📖 Read file:\x1b[0m ${filePath}`);
|
|
274
161
|
agentFeedback += `\n[FILE: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n`;
|
|
275
|
-
} catch (err) {
|
|
276
|
-
console.log(`\x1b[31m❌ Failed to read:\x1b[0m ${filePath}`);
|
|
277
|
-
agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`;
|
|
278
|
-
}
|
|
162
|
+
} catch (err) { agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`; }
|
|
279
163
|
}
|
|
280
164
|
|
|
281
|
-
// Tool: Search Web with sources
|
|
282
165
|
const searchRegex = /<search>([\s\S]*?)<\/search>/g;
|
|
283
166
|
while ((match = searchRegex.exec(fullResponse)) !== null) {
|
|
284
167
|
const query = match[1].trim();
|
|
285
168
|
console.log(`\x1b[35m🔍 Searching Web:\x1b[0m ${query}`);
|
|
286
169
|
try {
|
|
287
|
-
const
|
|
170
|
+
const searchRes = await fetch(BASE_URL + '/api/search', {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
|
|
173
|
+
body: JSON.stringify({ query })
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// 🚨 SAFE JSON PARSING TO PREVENT HTML CRASHES
|
|
177
|
+
const responseText = await searchRes.text();
|
|
178
|
+
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
|
+
|
|
185
|
+
if (!searchRes.ok) throw new Error(searchData.error || "Search failed");
|
|
186
|
+
|
|
187
|
+
let snippets = "No results found.";
|
|
288
188
|
if (searchData.items) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
snippet: i.
|
|
293
|
-
}));
|
|
294
|
-
|
|
295
|
-
console.log(`\x1b[35m📚 Sources found:\x1b[0m`);
|
|
296
|
-
searchSources.forEach((s, i) => {
|
|
297
|
-
console.log(` \x1b[36m${i+1}. ${s.title}\x1b[0m`);
|
|
298
|
-
console.log(` \x1b[90m${s.link}\x1b[0m`);
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
const snippets = searchData.items.map(i => `- ${i.title}: ${i.snippet}\n Source: ${i.link}`).join('\n');
|
|
302
|
-
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n`;
|
|
303
|
-
} else {
|
|
304
|
-
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\nNo results found.\n`;
|
|
189
|
+
console.log(`\x1b[32m📚 Sources Extracted:\x1b[0m`);
|
|
190
|
+
snippets = searchData.items.map(i => {
|
|
191
|
+
console.log(` - ${i.title}\n \x1b[31m${i.link}\x1b[0m`);
|
|
192
|
+
return `- Title: ${i.title}\n Snippet: ${i.snippet}\n URL: ${i.link}`;
|
|
193
|
+
}).join('\n\n');
|
|
305
194
|
}
|
|
306
|
-
|
|
307
|
-
|
|
195
|
+
agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n`;
|
|
196
|
+
} catch (err) {
|
|
197
|
+
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`;
|
|
308
199
|
}
|
|
309
200
|
}
|
|
310
201
|
|
|
311
|
-
// Tool: Write Files
|
|
312
202
|
const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
|
|
313
203
|
while ((match = fileRegex.exec(fullResponse)) !== null) {
|
|
314
204
|
const filePath = match[1], content = match[2];
|
|
@@ -318,23 +208,17 @@ async function chat(prompt, depth = 0) {
|
|
|
318
208
|
console.log(`\x1b[32m✔ Wrote file:\x1b[0m ${filePath}`);
|
|
319
209
|
}
|
|
320
210
|
|
|
321
|
-
// Tool: Execute Commands
|
|
322
211
|
const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
|
|
323
212
|
const commands = [];
|
|
324
213
|
while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
|
|
325
|
-
|
|
326
214
|
for (const cmd of commands) {
|
|
327
|
-
try {
|
|
328
|
-
|
|
329
|
-
} catch (err) {
|
|
330
|
-
console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
|
|
331
|
-
agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`;
|
|
332
|
-
}
|
|
215
|
+
try { await runTerminalCommand(cmd); }
|
|
216
|
+
catch (err) { console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`); agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`; }
|
|
333
217
|
}
|
|
334
218
|
|
|
335
219
|
if (agentFeedback) {
|
|
336
|
-
console.log(`\
|
|
337
|
-
await chat(`
|
|
220
|
+
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);
|
|
338
222
|
return;
|
|
339
223
|
}
|
|
340
224
|
|
|
@@ -342,106 +226,52 @@ async function chat(prompt, depth = 0) {
|
|
|
342
226
|
console.log(`\x1b[32m✨ Task Completed!\x1b[0m`);
|
|
343
227
|
console.log('\x1b[32m════════════════════════════════════════\x1b[0m\n');
|
|
344
228
|
|
|
345
|
-
} catch (error) {
|
|
346
|
-
stopSpinner();
|
|
347
|
-
console.error('\n❌ Connection error: ' + error.message);
|
|
348
|
-
}
|
|
229
|
+
} catch (error) { stopSpinner(); console.error('\n❌ Connection error: ' + error.message); }
|
|
349
230
|
}
|
|
350
231
|
|
|
351
232
|
function startInteractiveMode() {
|
|
352
233
|
const config = loadConfig();
|
|
353
234
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
354
235
|
printLogo();
|
|
355
|
-
|
|
356
|
-
const agentStatus = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
|
|
357
|
-
|
|
358
236
|
console.log(`\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m`);
|
|
359
|
-
console.log(`
|
|
360
|
-
console.log(`
|
|
361
|
-
console.log(`\nCommands:`);
|
|
362
|
-
console.log(` \x1b[36m/agent\x1b[0m - Toggle Agent Mode (file/code operations)`);
|
|
363
|
-
console.log(` \x1b[36m/model\x1b[0m - Change AI Model`);
|
|
364
|
-
console.log(` \x1b[36m/clear\x1b[0m - Clear screen`);
|
|
365
|
-
console.log(` \x1b[36m/exit\x1b[0m - Exit\n`);
|
|
366
|
-
console.log(`\x1b[90mTip: In Chat mode, AI responds normally. In Agent mode, it can read/write files and run commands.\x1b[0m\n`);
|
|
237
|
+
console.log(`Model: \x1b[32m${config.model}\x1b[0m | Agent Mode: ${config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m'}`);
|
|
238
|
+
console.log(`Commands: \x1b[36m/model\x1b[0m, \x1b[36m/agent\x1b[0m, \x1b[36m/clear\x1b[0m, \x1b[36m/exit\x1b[0m\n`);
|
|
367
239
|
|
|
368
240
|
function ask() {
|
|
369
|
-
|
|
370
|
-
rl.question(prompt, async (input) => {
|
|
241
|
+
rl.question('\x1b[36mNoxyAI > \x1b[0m', async (input) => {
|
|
371
242
|
const trimmed = input.trim();
|
|
372
243
|
const lower = trimmed.toLowerCase();
|
|
373
244
|
|
|
374
|
-
if (lower === 'exit' || lower === '/exit')
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
console.clear();
|
|
380
|
-
printLogo();
|
|
381
|
-
ask();
|
|
382
|
-
}
|
|
383
|
-
else if (lower === '/agent' || lower === '/agent on' || lower === '/agent off') {
|
|
384
|
-
config.agentMode = !config.agentMode;
|
|
385
|
-
saveConfig(config);
|
|
386
|
-
const status = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
|
|
387
|
-
console.log(`\n\x1b[33m🤖 Agent Mode: ${status}\x1b[0m`);
|
|
388
|
-
if (config.agentMode) {
|
|
389
|
-
console.log('\x1b[36mAgent mode features:\x1b[0m');
|
|
390
|
-
console.log(' • Read & write files');
|
|
391
|
-
console.log(' • Execute terminal commands');
|
|
392
|
-
console.log(' • Web search integration');
|
|
393
|
-
console.log(' • Multi-step autonomous tasks\n');
|
|
394
|
-
} else {
|
|
395
|
-
console.log('\x1b[36mChat mode: Normal AI conversation without file access\x1b[0m\n');
|
|
396
|
-
}
|
|
245
|
+
if (lower === 'exit' || lower === '/exit') process.exit(0);
|
|
246
|
+
else if (lower === '/clear') { console.clear(); printLogo(); ask(); }
|
|
247
|
+
else if (lower === '/agent') {
|
|
248
|
+
const c = loadConfig(); c.agentMode = !c.agentMode; saveConfig(c);
|
|
249
|
+
console.log(`\n\x1b[33mAgent Mode is now ${c.agentMode ? '\x1b[32mON (File/Web access enabled)' : '\x1b[31mOFF (Standard Chat)'}\x1b[0m\n`);
|
|
397
250
|
ask();
|
|
398
251
|
}
|
|
399
252
|
else if (lower === '/model') {
|
|
400
253
|
console.log('\n\x1b[33mSelect an AI Model:\x1b[0m');
|
|
401
|
-
console.log(' \x1b[36m1)\x1b[0m Auto (
|
|
402
|
-
console.log(' \x1b[36m2)\x1b[0m Qwen3 80B
|
|
403
|
-
console.log(' \x1b[36m3)\x1b[0m Mamba
|
|
404
|
-
|
|
254
|
+
console.log(' \x1b[36m1)\x1b[0m Auto (Llama 3.3 70B)');
|
|
255
|
+
console.log(' \x1b[36m2)\x1b[0m Qwen3 Next 80B (Deep Reasoning)');
|
|
256
|
+
console.log(' \x1b[36m3)\x1b[0m Mistral Mamba Codestral 7B');
|
|
405
257
|
rl.question('\nEnter number (1-3): ', (choice) => {
|
|
406
258
|
let selected = 'auto';
|
|
407
259
|
if (choice === '2') selected = 'Qwen3';
|
|
408
|
-
if (choice === '3') selected = 'Mamba
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
saveConfig(config);
|
|
412
|
-
|
|
413
|
-
console.log(`\n\x1b[32m✔ Model changed to: ${selected}\x1b[0m\n`);
|
|
414
|
-
ask();
|
|
260
|
+
if (choice === '3') selected = 'Mamba';
|
|
261
|
+
const c = loadConfig(); c.model = selected; saveConfig(c);
|
|
262
|
+
console.log(`\n\x1b[32m✔ Model changed to: ${selected}\x1b[0m\n`); ask();
|
|
415
263
|
});
|
|
416
264
|
return;
|
|
417
265
|
}
|
|
418
|
-
else if (trimmed) {
|
|
419
|
-
|
|
420
|
-
ask();
|
|
421
|
-
} else {
|
|
422
|
-
ask();
|
|
423
|
-
}
|
|
266
|
+
else if (trimmed) { await chat(trimmed); ask(); }
|
|
267
|
+
else ask();
|
|
424
268
|
});
|
|
425
269
|
}
|
|
426
270
|
ask();
|
|
427
271
|
}
|
|
428
272
|
|
|
429
|
-
// Command handling
|
|
430
273
|
if (command === 'login') login();
|
|
431
|
-
else if (command === 'logout')
|
|
432
|
-
else if (command === 'help' || command === '--help') {
|
|
433
|
-
printLogo();
|
|
434
|
-
console.log(`\n \x1b[32mnoxyai\x1b[0m Start Interactive Mode`);
|
|
435
|
-
console.log(` \x1b[32mnoxyai chat "<prompt>"\x1b[0m Run single prompt`);
|
|
436
|
-
console.log(`\n Interactive Commands:`);
|
|
437
|
-
console.log(` \x1b[36m/agent\x1b[0m Toggle Agent Mode (file/code operations)`);
|
|
438
|
-
console.log(` \x1b[36m/model\x1b[0m Change AI Model\n`);
|
|
439
|
-
}
|
|
440
|
-
else if (command === 'chat') {
|
|
441
|
-
chat(args.slice(1).join(' ')).then(() => process.exit(0));
|
|
442
|
-
}
|
|
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`); }
|
|
443
276
|
else if (!command) startInteractiveMode();
|
|
444
|
-
else {
|
|
445
|
-
console.error(`❌ Unknown command. Run "noxyai help"`);
|
|
446
|
-
process.exit(1);
|
|
447
|
-
}
|
|
277
|
+
else { console.error(`❌ Unknown command.`); process.exit(1); }
|