natureco-cli 1.0.11 → 1.0.13
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/bin/natureco.js +16 -0
- package/package.json +3 -2
- package/src/commands/chat.js +73 -2
- package/src/commands/gateway-server.js +120 -0
- package/src/commands/gateway.js +26 -5
- package/src/commands/ultrareview.js +146 -0
- package/src/utils/gateway-ws.js +89 -0
- package/src/utils/history.js +6 -0
- package/src/utils/memory.js +52 -9
package/bin/natureco.js
CHANGED
|
@@ -144,6 +144,22 @@ program
|
|
|
144
144
|
tasksCmd(action, ...(params || []));
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
program
|
|
148
|
+
.command('gateway <action>')
|
|
149
|
+
.description('WebSocket gateway server (start|stop|status)')
|
|
150
|
+
.action((action) => {
|
|
151
|
+
const gatewayCmd = require('../src/commands/gateway-server');
|
|
152
|
+
gatewayCmd(action);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command('ultrareview [file]')
|
|
157
|
+
.description('Ultra detailed code review')
|
|
158
|
+
.action((file) => {
|
|
159
|
+
const ultrareviewCmd = require('../src/commands/ultrareview');
|
|
160
|
+
ultrareviewCmd(file);
|
|
161
|
+
});
|
|
162
|
+
|
|
147
163
|
program
|
|
148
164
|
.command('help')
|
|
149
165
|
.description('Show help information')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "NatureCo AI Bot Terminal Interface",
|
|
5
5
|
"main": "bin/natureco.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"conf": "^10.2.0",
|
|
31
31
|
"inquirer": "^8.2.7",
|
|
32
32
|
"node-cron": "^2.0.3",
|
|
33
|
-
"ora": "^5.4.1"
|
|
33
|
+
"ora": "^5.4.1",
|
|
34
|
+
"ws": "^8.20.0"
|
|
34
35
|
},
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=16.0.0"
|
package/src/commands/chat.js
CHANGED
|
@@ -282,10 +282,18 @@ async function chat(botName, options = {}) {
|
|
|
282
282
|
console.log(chalk.cyan(' İsim:'), chalk.white(memory.name));
|
|
283
283
|
}
|
|
284
284
|
if (memory.preferences.length > 0) {
|
|
285
|
-
|
|
285
|
+
const sortedPrefs = memory.preferences.sort((a, b) => b.score - a.score);
|
|
286
|
+
console.log(chalk.cyan(' Tercihler:'));
|
|
287
|
+
sortedPrefs.forEach(p => {
|
|
288
|
+
console.log(chalk.white(` • ${p.value}`) + chalk.gray(` (skor: ${p.score})`));
|
|
289
|
+
});
|
|
286
290
|
}
|
|
287
291
|
if (memory.facts.length > 0) {
|
|
288
|
-
|
|
292
|
+
const sortedFacts = memory.facts.sort((a, b) => b.score - a.score);
|
|
293
|
+
console.log(chalk.cyan(' Bilgiler:'));
|
|
294
|
+
sortedFacts.forEach(f => {
|
|
295
|
+
console.log(chalk.white(` • ${f.value}`) + chalk.gray(` (skor: ${f.score})`));
|
|
296
|
+
});
|
|
289
297
|
}
|
|
290
298
|
if (!memory.name && memory.preferences.length === 0 && memory.facts.length === 0) {
|
|
291
299
|
console.log(chalk.gray(' Henüz hafıza yok'));
|
|
@@ -318,6 +326,7 @@ async function chat(botName, options = {}) {
|
|
|
318
326
|
console.log(chalk.cyan(' /memory') + chalk.gray(' - Hafızayı göster'));
|
|
319
327
|
console.log(chalk.cyan(' /memory clear') + chalk.gray(' - Hafızayı temizle'));
|
|
320
328
|
console.log(chalk.cyan(' /commands') + chalk.gray(' - Özel komutları listele'));
|
|
329
|
+
console.log(chalk.cyan(' /ultrareview') + chalk.gray(' - Son kod bloğunu detaylı incele'));
|
|
321
330
|
console.log(chalk.cyan(' /help') + chalk.gray(' - Bu yardım mesajını göster'));
|
|
322
331
|
console.log(chalk.cyan(' exit, quit') + chalk.gray(' - Chat\'ten çık'));
|
|
323
332
|
console.log(chalk.gray('\nYukarı/aşağı ok tuşları ile komut geçmişinde gezin.'));
|
|
@@ -325,6 +334,68 @@ async function chat(botName, options = {}) {
|
|
|
325
334
|
rl.prompt();
|
|
326
335
|
return;
|
|
327
336
|
|
|
337
|
+
case 'ultrareview':
|
|
338
|
+
// Extract last code block from conversation
|
|
339
|
+
const historyData = require('../utils/history');
|
|
340
|
+
const chatHistory = historyData.getHistory ? historyData.getHistory(bot.id) : [];
|
|
341
|
+
|
|
342
|
+
let lastCodeBlock = null;
|
|
343
|
+
for (let i = chatHistory.length - 1; i >= 0; i--) {
|
|
344
|
+
const msg = chatHistory[i];
|
|
345
|
+
const codeMatch = (msg.bot || '').match(/```[\s\S]*?\n([\s\S]*?)```/);
|
|
346
|
+
if (codeMatch) {
|
|
347
|
+
lastCodeBlock = codeMatch[1];
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!lastCodeBlock) {
|
|
353
|
+
console.log(chalk.yellow('\n⚠️ Konuşmada kod bloğu bulunamadı\n'));
|
|
354
|
+
rl.prompt();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
console.log(chalk.yellow('\n⏳ Kod inceleniyor...\n'));
|
|
359
|
+
|
|
360
|
+
const reviewPrompt = `Aşağıdaki kodu çok detaylı incele ve şu kategorilerde analiz et:
|
|
361
|
+
|
|
362
|
+
1. **Güvenlik Açıkları** (1-10 puan)
|
|
363
|
+
2. **Performans Sorunları** (1-10 puan)
|
|
364
|
+
3. **Kod Kalitesi** (1-10 puan)
|
|
365
|
+
4. **Hata Yönetimi** (1-10 puan)
|
|
366
|
+
5. **Best Practices** (1-10 puan)
|
|
367
|
+
6. **Potansiyel Bug'lar**
|
|
368
|
+
|
|
369
|
+
Her kategori için puan ver ve sorunları listele.
|
|
370
|
+
|
|
371
|
+
\`\`\`
|
|
372
|
+
${lastCodeBlock}
|
|
373
|
+
\`\`\``;
|
|
374
|
+
|
|
375
|
+
const loadingInterval = startLoadingAnimation();
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const response = await sendMessage(apiKey, bot.id, reviewPrompt, conversationId, systemPrompt);
|
|
379
|
+
|
|
380
|
+
stopLoadingAnimation(loadingInterval);
|
|
381
|
+
|
|
382
|
+
if (response.conversation_id) {
|
|
383
|
+
conversationId = response.conversation_id;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const botReply = response.reply || response.message || 'No response';
|
|
387
|
+
console.log(chalk.green(`bot › ${botReply}\n`));
|
|
388
|
+
|
|
389
|
+
addToHistory(bot.id, '/ultrareview', botReply, conversationId);
|
|
390
|
+
addMessageToSession(bot.id, session.id, '/ultrareview', botReply);
|
|
391
|
+
} catch (err) {
|
|
392
|
+
stopLoadingAnimation(loadingInterval);
|
|
393
|
+
console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
rl.prompt();
|
|
397
|
+
return;
|
|
398
|
+
|
|
328
399
|
default:
|
|
329
400
|
// Check for custom commands
|
|
330
401
|
const customCommand = getCommandContent(command);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const PID_FILE = path.join(os.homedir(), '.natureco', 'gateway.pid');
|
|
8
|
+
const LOG_FILE = path.join(os.homedir(), '.natureco', 'gateway.log');
|
|
9
|
+
|
|
10
|
+
async function gatewayServer(action) {
|
|
11
|
+
if (!action || action === 'start') {
|
|
12
|
+
return startGateway();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (action === 'stop') {
|
|
16
|
+
return stopGateway();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (action === 'status') {
|
|
20
|
+
return statusGateway();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(chalk.red('\n❌ Unknown action\n'));
|
|
24
|
+
console.log(chalk.gray('Available actions: start, stop, status\n'));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function startGateway() {
|
|
29
|
+
// Check if already running
|
|
30
|
+
if (fs.existsSync(PID_FILE)) {
|
|
31
|
+
const pid = fs.readFileSync(PID_FILE, 'utf-8').trim();
|
|
32
|
+
try {
|
|
33
|
+
process.kill(pid, 0);
|
|
34
|
+
console.log(chalk.yellow('\n⚠️ Gateway already running\n'));
|
|
35
|
+
console.log(chalk.gray(`PID: ${pid}`));
|
|
36
|
+
console.log(chalk.gray('Stop with: natureco gateway stop\n'));
|
|
37
|
+
return;
|
|
38
|
+
} catch {
|
|
39
|
+
// Process not running, remove stale PID file
|
|
40
|
+
fs.unlinkSync(PID_FILE);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(chalk.green('\n🚀 Starting WebSocket Gateway...\n'));
|
|
45
|
+
|
|
46
|
+
// Start gateway server as detached process
|
|
47
|
+
const serverPath = path.join(__dirname, '..', 'utils', 'gateway-ws.js');
|
|
48
|
+
const child = spawn('node', [serverPath], {
|
|
49
|
+
detached: true,
|
|
50
|
+
stdio: 'ignore',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
child.unref();
|
|
54
|
+
|
|
55
|
+
// Save PID
|
|
56
|
+
const dir = path.dirname(PID_FILE);
|
|
57
|
+
if (!fs.existsSync(dir)) {
|
|
58
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
fs.writeFileSync(PID_FILE, String(child.pid), 'utf-8');
|
|
61
|
+
|
|
62
|
+
console.log(chalk.green('✅ Gateway started'));
|
|
63
|
+
console.log(chalk.cyan('Port:'), chalk.white('3847'));
|
|
64
|
+
console.log(chalk.cyan('PID:'), chalk.white(child.pid));
|
|
65
|
+
console.log(chalk.gray('\nConnect with WebSocket client:'));
|
|
66
|
+
console.log(chalk.white('ws://localhost:3847'));
|
|
67
|
+
console.log(chalk.gray('\nSend JSON: { "botId": "...", "message": "...", "apiKey": "..." }'));
|
|
68
|
+
console.log(chalk.gray('\nStop with: natureco gateway stop\n'));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function stopGateway() {
|
|
72
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
73
|
+
console.log(chalk.gray('\n⚠️ Gateway not running\n'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const pid = fs.readFileSync(PID_FILE, 'utf-8').trim();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
process.kill(pid, 'SIGTERM');
|
|
81
|
+
fs.unlinkSync(PID_FILE);
|
|
82
|
+
console.log(chalk.green('\n✅ Gateway stopped\n'));
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.log(chalk.red(`\n❌ Failed to stop gateway: ${err.message}\n`));
|
|
85
|
+
// Remove stale PID file
|
|
86
|
+
if (fs.existsSync(PID_FILE)) {
|
|
87
|
+
fs.unlinkSync(PID_FILE);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function statusGateway() {
|
|
93
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
94
|
+
console.log(chalk.gray('\n⚠️ Gateway not running\n'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const pid = fs.readFileSync(PID_FILE, 'utf-8').trim();
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
process.kill(pid, 0);
|
|
102
|
+
console.log(chalk.green('\n✅ Gateway running\n'));
|
|
103
|
+
console.log(chalk.cyan('PID:'), chalk.white(pid));
|
|
104
|
+
console.log(chalk.cyan('Port:'), chalk.white('3847'));
|
|
105
|
+
console.log(chalk.cyan('WebSocket:'), chalk.white('ws://localhost:3847'));
|
|
106
|
+
|
|
107
|
+
// Show log tail if exists
|
|
108
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
109
|
+
const logs = fs.readFileSync(LOG_FILE, 'utf-8').split('\n').slice(-5).join('\n');
|
|
110
|
+
console.log(chalk.gray('\nRecent logs:'));
|
|
111
|
+
console.log(chalk.white(logs));
|
|
112
|
+
}
|
|
113
|
+
console.log('');
|
|
114
|
+
} catch {
|
|
115
|
+
console.log(chalk.gray('\n⚠️ Gateway not running (stale PID file)\n'));
|
|
116
|
+
fs.unlinkSync(PID_FILE);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = gatewayServer;
|
package/src/commands/gateway.js
CHANGED
|
@@ -27,8 +27,8 @@ async function gateway() {
|
|
|
27
27
|
|
|
28
28
|
console.clear();
|
|
29
29
|
|
|
30
|
-
// Ana logo kutusu
|
|
31
|
-
const
|
|
30
|
+
// Ana logo kutusu
|
|
31
|
+
const titleBox = boxen(
|
|
32
32
|
chalk.green.bold('🌿 NatureCo Terminal\n') +
|
|
33
33
|
chalk.green.bold(` v${version}\n\n`) +
|
|
34
34
|
chalk.gray('AI botlarınızla terminal\'den konuşun\n') +
|
|
@@ -36,14 +36,35 @@ async function gateway() {
|
|
|
36
36
|
chalk.gray('Yetenekler ekleyin, MCP entegre edin'),
|
|
37
37
|
{
|
|
38
38
|
padding: { top: 1, bottom: 1, left: 3, right: 3 },
|
|
39
|
-
margin: { top:
|
|
39
|
+
margin: { top: 0, bottom: 0, left: 0, right: 0 },
|
|
40
40
|
borderStyle: 'round',
|
|
41
41
|
borderColor: 'green',
|
|
42
42
|
align: 'center',
|
|
43
43
|
}
|
|
44
44
|
);
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
// Kutuyu satırlara böl
|
|
47
|
+
const boxLines = titleBox.split('\n');
|
|
48
|
+
|
|
49
|
+
// Tavşan satırları
|
|
50
|
+
const bunnyLines = [
|
|
51
|
+
'',
|
|
52
|
+
chalk.hex('#69F0AE').bold(' (\\ /)'),
|
|
53
|
+
chalk.hex('#69F0AE').bold(' ( . .)'),
|
|
54
|
+
chalk.hex('#69F0AE').bold(' c(")(")'),
|
|
55
|
+
'',
|
|
56
|
+
'',
|
|
57
|
+
'',
|
|
58
|
+
'',
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Tavşan ve kutuyu yan yana yazdır
|
|
62
|
+
const maxLines = Math.max(bunnyLines.length, boxLines.length);
|
|
63
|
+
for (let i = 0; i < maxLines; i++) {
|
|
64
|
+
const bunnyLine = bunnyLines[i] || '';
|
|
65
|
+
const boxLine = boxLines[i] || '';
|
|
66
|
+
console.log(bunnyLine + ' ' + boxLine);
|
|
67
|
+
}
|
|
47
68
|
console.log(chalk.gray('─'.repeat(60)));
|
|
48
69
|
console.log('');
|
|
49
70
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { getApiKey, getConfig } = require('../utils/config');
|
|
6
|
+
const { getBots, sendMessage } = require('../utils/api');
|
|
7
|
+
|
|
8
|
+
const REVIEWS_DIR = path.join(os.homedir(), '.natureco', 'reviews');
|
|
9
|
+
|
|
10
|
+
async function ultrareview(filePath) {
|
|
11
|
+
const apiKey = getApiKey();
|
|
12
|
+
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const config = getConfig();
|
|
19
|
+
const defaultBotId = config.defaultBotId;
|
|
20
|
+
|
|
21
|
+
if (!defaultBotId) {
|
|
22
|
+
console.log(chalk.red('\n❌ No default bot configured.\n'));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let code;
|
|
27
|
+
let filename;
|
|
28
|
+
|
|
29
|
+
// Read from file or stdin
|
|
30
|
+
if (filePath) {
|
|
31
|
+
if (!fs.existsSync(filePath)) {
|
|
32
|
+
console.log(chalk.red(`\n❌ File not found: ${filePath}\n`));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
code = fs.readFileSync(filePath, 'utf-8');
|
|
36
|
+
filename = path.basename(filePath);
|
|
37
|
+
} else {
|
|
38
|
+
// Read from stdin
|
|
39
|
+
console.log(chalk.yellow('Reading from stdin... (Ctrl+D to finish)\n'));
|
|
40
|
+
const chunks = [];
|
|
41
|
+
for await (const chunk of process.stdin) {
|
|
42
|
+
chunks.push(chunk);
|
|
43
|
+
}
|
|
44
|
+
code = Buffer.concat(chunks).toString('utf-8');
|
|
45
|
+
filename = 'stdin';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!code || code.trim().length === 0) {
|
|
49
|
+
console.log(chalk.red('\n❌ No code provided\n'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(chalk.yellow('\n⏳ Analyzing code...\n'));
|
|
54
|
+
|
|
55
|
+
const prompt = `Aşağıdaki kodu çok detaylı incele ve şu kategorilerde analiz et:
|
|
56
|
+
|
|
57
|
+
1. **Güvenlik Açıkları** (1-10 puan)
|
|
58
|
+
- SQL injection, XSS, CSRF, auth bypass
|
|
59
|
+
- Input validation eksiklikleri
|
|
60
|
+
- Sensitive data exposure
|
|
61
|
+
|
|
62
|
+
2. **Performans Sorunları** (1-10 puan)
|
|
63
|
+
- N+1 query problemi
|
|
64
|
+
- Memory leak riski
|
|
65
|
+
- Blocking operations
|
|
66
|
+
- Gereksiz hesaplamalar
|
|
67
|
+
|
|
68
|
+
3. **Kod Kalitesi** (1-10 puan)
|
|
69
|
+
- SOLID prensipleri
|
|
70
|
+
- DRY (Don't Repeat Yourself)
|
|
71
|
+
- Complexity (cyclomatic)
|
|
72
|
+
- Okunabilirlik
|
|
73
|
+
|
|
74
|
+
4. **Hata Yönetimi** (1-10 puan)
|
|
75
|
+
- Unhandled exceptions
|
|
76
|
+
- Missing validation
|
|
77
|
+
- Error handling best practices
|
|
78
|
+
|
|
79
|
+
5. **Best Practices** (1-10 puan)
|
|
80
|
+
- Naming conventions
|
|
81
|
+
- Comments ve documentation
|
|
82
|
+
- Test coverage potansiyeli
|
|
83
|
+
- Code organization
|
|
84
|
+
|
|
85
|
+
6. **Potansiyel Bug'lar**
|
|
86
|
+
- Edge case'ler
|
|
87
|
+
- Race conditions
|
|
88
|
+
- Null/undefined handling
|
|
89
|
+
|
|
90
|
+
Her kategori için:
|
|
91
|
+
- Puan ver (1-10)
|
|
92
|
+
- Sorunları listele
|
|
93
|
+
- Çözüm önerileri sun
|
|
94
|
+
|
|
95
|
+
Sonunda özet tablo göster.
|
|
96
|
+
|
|
97
|
+
\`\`\`
|
|
98
|
+
${code}
|
|
99
|
+
\`\`\``;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const response = await sendMessage(apiKey, defaultBotId, prompt, null, '');
|
|
103
|
+
const review = response.reply || response.message || 'No response';
|
|
104
|
+
|
|
105
|
+
console.log(chalk.green('\n📊 Ultra Review:\n'));
|
|
106
|
+
console.log(chalk.white(review));
|
|
107
|
+
console.log('');
|
|
108
|
+
|
|
109
|
+
// Save to file
|
|
110
|
+
if (!fs.existsSync(REVIEWS_DIR)) {
|
|
111
|
+
fs.mkdirSync(REVIEWS_DIR, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
115
|
+
const reviewFilename = `${timestamp}-${filename}.md`;
|
|
116
|
+
const reviewPath = path.join(REVIEWS_DIR, reviewFilename);
|
|
117
|
+
|
|
118
|
+
const reviewContent = `# Ultra Review: ${filename}
|
|
119
|
+
|
|
120
|
+
**Date:** ${new Date().toLocaleString()}
|
|
121
|
+
**File:** ${filePath || 'stdin'}
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
${review}
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Original Code
|
|
130
|
+
|
|
131
|
+
\`\`\`
|
|
132
|
+
${code}
|
|
133
|
+
\`\`\`
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
fs.writeFileSync(reviewPath, reviewContent, 'utf-8');
|
|
137
|
+
|
|
138
|
+
console.log(chalk.cyan('💾 Review saved:'), chalk.white(reviewPath));
|
|
139
|
+
console.log('');
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = ultrareview;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const PORT = 3847;
|
|
7
|
+
const LOG_FILE = path.join(os.homedir(), '.natureco', 'gateway.log');
|
|
8
|
+
|
|
9
|
+
function log(message) {
|
|
10
|
+
const timestamp = new Date().toISOString();
|
|
11
|
+
const logMessage = `[${timestamp}] ${message}\n`;
|
|
12
|
+
fs.appendFileSync(LOG_FILE, logMessage, 'utf-8');
|
|
13
|
+
console.log(logMessage.trim());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const wss = new WebSocket.Server({ port: PORT });
|
|
17
|
+
|
|
18
|
+
let connectedClients = 0;
|
|
19
|
+
|
|
20
|
+
wss.on('connection', (ws) => {
|
|
21
|
+
connectedClients++;
|
|
22
|
+
log(`Client connected. Total clients: ${connectedClients}`);
|
|
23
|
+
|
|
24
|
+
ws.on('message', async (data) => {
|
|
25
|
+
try {
|
|
26
|
+
const message = JSON.parse(data.toString());
|
|
27
|
+
const { botId, message: userMessage, apiKey } = message;
|
|
28
|
+
|
|
29
|
+
if (!botId || !userMessage || !apiKey) {
|
|
30
|
+
ws.send(JSON.stringify({ error: 'Missing required fields: botId, message, apiKey' }));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
log(`Message received for bot ${botId}: ${userMessage.slice(0, 50)}...`);
|
|
35
|
+
|
|
36
|
+
// Send to NatureCo API
|
|
37
|
+
const response = await fetch('https://api.natureco.me/api/agent/chat', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
agent_id: botId,
|
|
45
|
+
message: userMessage,
|
|
46
|
+
platform: 'gateway',
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const error = await response.text();
|
|
52
|
+
ws.send(JSON.stringify({ error: `API error: ${error}` }));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const data = await response.json();
|
|
57
|
+
const reply = data.reply || data.message || 'No response';
|
|
58
|
+
|
|
59
|
+
log(`Reply sent: ${reply.slice(0, 50)}...`);
|
|
60
|
+
|
|
61
|
+
ws.send(JSON.stringify({
|
|
62
|
+
reply,
|
|
63
|
+
sessionId: data.conversation_id || null,
|
|
64
|
+
}));
|
|
65
|
+
} catch (err) {
|
|
66
|
+
log(`Error: ${err.message}`);
|
|
67
|
+
ws.send(JSON.stringify({ error: err.message }));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
ws.on('close', () => {
|
|
72
|
+
connectedClients--;
|
|
73
|
+
log(`Client disconnected. Total clients: ${connectedClients}`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
ws.on('error', (err) => {
|
|
77
|
+
log(`WebSocket error: ${err.message}`);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
log(`WebSocket Gateway started on port ${PORT}`);
|
|
82
|
+
|
|
83
|
+
process.on('SIGTERM', () => {
|
|
84
|
+
log('Gateway shutting down...');
|
|
85
|
+
wss.close(() => {
|
|
86
|
+
log('Gateway stopped');
|
|
87
|
+
process.exit(0);
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/utils/history.js
CHANGED
|
@@ -78,10 +78,16 @@ function clearHistory(botId) {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// Get full history (for ultrareview)
|
|
82
|
+
function getHistory(botId) {
|
|
83
|
+
return loadHistory(botId);
|
|
84
|
+
}
|
|
85
|
+
|
|
81
86
|
module.exports = {
|
|
82
87
|
loadHistory,
|
|
83
88
|
saveHistory,
|
|
84
89
|
addToHistory,
|
|
85
90
|
getCommandHistory,
|
|
86
91
|
clearHistory,
|
|
92
|
+
getHistory,
|
|
87
93
|
};
|
package/src/utils/memory.js
CHANGED
|
@@ -32,7 +32,17 @@ function loadMemory(botId) {
|
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
34
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
35
|
-
|
|
35
|
+
const memory = JSON.parse(content);
|
|
36
|
+
|
|
37
|
+
// Migrate old format to new format
|
|
38
|
+
if (Array.isArray(memory.preferences) && memory.preferences.length > 0 && typeof memory.preferences[0] === 'string') {
|
|
39
|
+
memory.preferences = memory.preferences.map(p => ({ value: p, score: 5, updatedAt: new Date().toISOString() }));
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(memory.facts) && memory.facts.length > 0 && typeof memory.facts[0] === 'string') {
|
|
42
|
+
memory.facts = memory.facts.map(f => ({ value: f, score: 5, updatedAt: new Date().toISOString() }));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return memory;
|
|
36
46
|
} catch {
|
|
37
47
|
return {
|
|
38
48
|
name: null,
|
|
@@ -55,22 +65,53 @@ function saveMemory(botId, memory) {
|
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
// Add memory entry
|
|
68
|
+
// Add memory entry with scoring
|
|
59
69
|
function addMemoryEntry(botId, key, value) {
|
|
60
70
|
const memory = loadMemory(botId);
|
|
71
|
+
const now = new Date().toISOString();
|
|
61
72
|
|
|
62
73
|
if (key === 'name') {
|
|
63
74
|
memory.name = value;
|
|
64
75
|
} else if (key === 'preference') {
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
// Check for duplicate
|
|
77
|
+
const existing = memory.preferences.find(p => p.value.toLowerCase() === value.toLowerCase());
|
|
78
|
+
if (existing) {
|
|
79
|
+
existing.score = Math.min(existing.score + 1, 10);
|
|
80
|
+
existing.updatedAt = now;
|
|
81
|
+
} else {
|
|
82
|
+
memory.preferences.push({ value, score: 5, updatedAt: now });
|
|
67
83
|
}
|
|
68
84
|
} else if (key === 'fact') {
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
// Check for duplicate
|
|
86
|
+
const existing = memory.facts.find(f => f.value.toLowerCase() === value.toLowerCase());
|
|
87
|
+
if (existing) {
|
|
88
|
+
existing.score = Math.min(existing.score + 1, 10);
|
|
89
|
+
existing.updatedAt = now;
|
|
90
|
+
} else {
|
|
91
|
+
memory.facts.push({ value, score: 5, updatedAt: now });
|
|
71
92
|
}
|
|
72
93
|
}
|
|
73
94
|
|
|
95
|
+
// Decay old entries
|
|
96
|
+
const sixMonthsAgo = new Date();
|
|
97
|
+
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
|
|
98
|
+
|
|
99
|
+
memory.preferences = memory.preferences.map(p => {
|
|
100
|
+
const age = new Date(p.updatedAt);
|
|
101
|
+
if (age < sixMonthsAgo) {
|
|
102
|
+
p.score = Math.max(p.score - 1, 1);
|
|
103
|
+
}
|
|
104
|
+
return p;
|
|
105
|
+
}).filter(p => p.score > 0);
|
|
106
|
+
|
|
107
|
+
memory.facts = memory.facts.map(f => {
|
|
108
|
+
const age = new Date(f.updatedAt);
|
|
109
|
+
if (age < sixMonthsAgo) {
|
|
110
|
+
f.score = Math.max(f.score - 1, 1);
|
|
111
|
+
}
|
|
112
|
+
return f;
|
|
113
|
+
}).filter(f => f.score > 0);
|
|
114
|
+
|
|
74
115
|
saveMemory(botId, memory);
|
|
75
116
|
}
|
|
76
117
|
|
|
@@ -116,7 +157,7 @@ function extractMemoryFromMessage(message) {
|
|
|
116
157
|
return extracted;
|
|
117
158
|
}
|
|
118
159
|
|
|
119
|
-
// Get memory as system prompt
|
|
160
|
+
// Get memory as system prompt (sorted by score)
|
|
120
161
|
function getMemoryPrompt(botId) {
|
|
121
162
|
const memory = loadMemory(botId);
|
|
122
163
|
|
|
@@ -131,11 +172,13 @@ function getMemoryPrompt(botId) {
|
|
|
131
172
|
}
|
|
132
173
|
|
|
133
174
|
if (memory.preferences.length > 0) {
|
|
134
|
-
|
|
175
|
+
const sorted = memory.preferences.sort((a, b) => b.score - a.score);
|
|
176
|
+
parts.push(`Tercihler: ${sorted.map(p => p.value).join(', ')}`);
|
|
135
177
|
}
|
|
136
178
|
|
|
137
179
|
if (memory.facts.length > 0) {
|
|
138
|
-
|
|
180
|
+
const sorted = memory.facts.sort((a, b) => b.score - a.score);
|
|
181
|
+
parts.push(`Bilgiler: ${sorted.map(f => f.value).join(', ')}`);
|
|
139
182
|
}
|
|
140
183
|
|
|
141
184
|
return parts.join('\n');
|