natureco-cli 2.16.5 → 2.17.1

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 CHANGED
@@ -244,6 +244,70 @@ program
244
244
  doctorCmd(args);
245
245
  });
246
246
 
247
+ program
248
+ .command('channels [action] [params...]')
249
+ .description('Manage connected channels (list|status|remove|logs)')
250
+ .action((action, params) => {
251
+ const channelsCmd = require('../src/commands/channels');
252
+ channelsCmd([action, ...(params || [])]);
253
+ });
254
+
255
+ program
256
+ .command('models [action] [params...]')
257
+ .description('Manage AI models (list|set|scan|aliases)')
258
+ .action((action, params) => {
259
+ const modelsCmd = require('../src/commands/models');
260
+ modelsCmd([action, ...(params || [])]);
261
+ });
262
+
263
+ program
264
+ .command('memory [action] [params...]')
265
+ .description('Manage memory (status|list|search|show|clear|index)')
266
+ .action((action, params) => {
267
+ const memoryCmd = require('../src/commands/memory-cmd');
268
+ memoryCmd([action, ...(params || [])]);
269
+ });
270
+
271
+ program
272
+ .command('logs')
273
+ .description('View and follow gateway logs')
274
+ .option('--follow, -f', 'Follow logs in real-time')
275
+ .option('--json', 'JSON output')
276
+ .option('--plain', 'Plain text output')
277
+ .option('--limit <n>', 'Number of lines to show', '50')
278
+ .action(() => {
279
+ const logsCmd = require('../src/commands/logs');
280
+ logsCmd(process.argv.slice(3));
281
+ });
282
+
283
+ program
284
+ .command('status')
285
+ .description('Show overall system status')
286
+ .action(() => {
287
+ const statusCmd = require('../src/commands/status');
288
+ statusCmd();
289
+ });
290
+
291
+ program
292
+ .command('reset')
293
+ .description('Reset local config/state')
294
+ .option('--scope <scope>', 'Scope: config|memory|sessions|full', 'config')
295
+ .option('--yes, -y', 'Skip confirmation')
296
+ .action(() => {
297
+ const resetCmd = require('../src/commands/reset');
298
+ resetCmd(process.argv.slice(3));
299
+ });
300
+
301
+ program
302
+ .command('security [action]')
303
+ .description('Security audit (audit)')
304
+ .option('--fix', 'Auto-fix issues')
305
+ .option('--json', 'JSON output')
306
+ .action((action) => {
307
+ const securityCmd = require('../src/commands/security');
308
+ securityCmd(process.argv.slice(3));
309
+ });
310
+
247
311
  program
248
312
  .command('help')
249
313
  .description('Show help information')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "2.16.5",
3
+ "version": "2.17.1",
4
4
  "description": "NatureCo AI Bot Terminal Interface",
5
5
  "main": "bin/natureco.js",
6
6
  "bin": {
@@ -3,37 +3,38 @@ const { getApiKey } = require('../utils/config');
3
3
  const { getBots } = require('../utils/api');
4
4
 
5
5
  async function bots() {
6
- const apiKey = getApiKey();
6
+ const { getConfig } = require('../utils/config');
7
+ const config = getConfig();
8
+ const apiKey = getApiKey() || config.providerApiKey || '';
7
9
 
8
- if (!apiKey) {
9
- console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
10
- process.exit(1);
11
- }
12
-
13
- console.log(chalk.yellow('\n⏳ Fetching your bots...\n'));
10
+ console.log(chalk.gray('\n Botlar yükleniyor...\n'));
14
11
 
15
12
  try {
16
13
  const botList = await getBots(apiKey);
17
14
 
18
15
  if (!botList || !botList.bots || botList.bots.length === 0) {
19
- console.log(chalk.gray('No bots found. Create one at https://developers.natureco.me\n'));
16
+ console.log(chalk.gray(' Bot bulunamadı.'));
17
+ console.log(chalk.gray(' Oluşturmak için: ') + chalk.cyan('developers.natureco.me\n'));
20
18
  return;
21
19
  }
22
20
 
23
- console.log(chalk.green.bold('╭─ Your Bots ─╮\n'));
21
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
22
+ console.log(chalk.cyan.bold('\n Botlarım\n'));
24
23
 
25
24
  botList.bots.forEach((bot, index) => {
26
- console.log(chalk.cyan(`${index + 1}. ${bot.name}`));
27
- console.log(chalk.gray(` ID: ${bot.id}`));
28
- console.log(chalk.gray(` Provider: ${bot.ai_provider || 'openai'}`));
29
- console.log(chalk.gray(` Public: ${bot.is_public ? 'Yes' : 'No'}`));
25
+ const active = config.botName === bot.name ? chalk.green(' ← aktif') : '';
26
+ console.log(chalk.white(` ${index + 1}. ${bot.name}`) + active);
27
+ console.log(chalk.gray(` ID : ${bot.id}`));
28
+ console.log(chalk.gray(` Provider: ${bot.ai_provider || 'groq'}`));
29
+ if (bot.model) console.log(chalk.gray(` Model : ${bot.model}`));
30
30
  console.log('');
31
31
  });
32
32
 
33
- console.log(chalk.green('╰──────────────╯\n'));
34
- console.log(chalk.gray(`Total: ${botList.bots.length} bot(s)\n`));
33
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
34
+ console.log(chalk.gray(` Toplam: ${botList.bots.length} bot`));
35
+ console.log(chalk.gray(' Chat başlatmak için: ') + chalk.cyan('natureco chat\n'));
35
36
  } catch (err) {
36
- console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
37
+ console.log(chalk.red(`\n Hata: ${err.message}\n`));
37
38
  process.exit(1);
38
39
  }
39
40
  }
@@ -0,0 +1,177 @@
1
+ const chalk = require('chalk');
2
+ const { getConfig, saveConfig } = require('../utils/config');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ async function channels(args) {
8
+ const [action, ...params] = (args || []);
9
+
10
+ if (!action || action === 'list') return listChannels();
11
+ if (action === 'status') return statusChannels();
12
+ if (action === 'remove') return removeChannel(params[0]);
13
+ if (action === 'logs') return channelLogs(params[0]);
14
+
15
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
16
+ console.log(chalk.gray(' Kullanım: natureco channels [list|status|remove|logs]\n'));
17
+ process.exit(1);
18
+ }
19
+
20
+ function listChannels() {
21
+ const config = getConfig();
22
+ const channels = [];
23
+
24
+ if (config.telegramToken) {
25
+ channels.push({
26
+ name: 'Telegram',
27
+ icon: '✈',
28
+ status: 'bağlı',
29
+ detail: `token: ${config.telegramToken.slice(0, 12)}...`,
30
+ chats: (config.telegramAllowedChats || []).length,
31
+ });
32
+ }
33
+
34
+ if (config.whatsappConnected) {
35
+ channels.push({
36
+ name: 'WhatsApp',
37
+ icon: '📱',
38
+ status: 'bağlı',
39
+ detail: config.whatsappPhone ? `+${config.whatsappPhone.split(':')[0]}` : 'bağlı',
40
+ chats: (config.whatsappAllowedNumbers || []).length,
41
+ });
42
+ }
43
+
44
+ if (config.discordToken) {
45
+ channels.push({
46
+ name: 'Discord',
47
+ icon: '🎮',
48
+ status: 'bağlı',
49
+ detail: `token: ${config.discordToken.slice(0, 12)}...`,
50
+ chats: 0,
51
+ });
52
+ }
53
+
54
+ if (config.slackToken) {
55
+ channels.push({
56
+ name: 'Slack',
57
+ icon: '💼',
58
+ status: 'bağlı',
59
+ detail: `token: ${config.slackToken.slice(0, 12)}...`,
60
+ chats: 0,
61
+ });
62
+ }
63
+
64
+ console.log('');
65
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
66
+ console.log(chalk.cyan.bold('\n Kanallar\n'));
67
+
68
+ if (channels.length === 0) {
69
+ console.log(chalk.gray(' Bağlı kanal yok.\n'));
70
+ console.log(chalk.gray(' Bağlamak için:'));
71
+ console.log(chalk.cyan(' natureco telegram connect'));
72
+ console.log(chalk.cyan(' natureco whatsapp connect'));
73
+ console.log(chalk.cyan(' natureco discord connect'));
74
+ console.log(chalk.cyan(' natureco slack connect\n'));
75
+ return;
76
+ }
77
+
78
+ channels.forEach(ch => {
79
+ console.log(chalk.white(` ${ch.icon} ${ch.name}`) + chalk.green(' ✓ ' + ch.status));
80
+ console.log(chalk.gray(` ${ch.detail}`));
81
+ if (ch.chats > 0) console.log(chalk.gray(` İzin listesi: ${ch.chats} kayıt`));
82
+ console.log('');
83
+ });
84
+
85
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
86
+ console.log(chalk.gray(` Toplam: ${channels.length} kanal bağlı`));
87
+ console.log(chalk.gray(' Kaldırmak için: ') + chalk.cyan('natureco channels remove <kanal>\n'));
88
+ }
89
+
90
+ function statusChannels() {
91
+ const config = getConfig();
92
+ console.log('');
93
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
94
+ console.log(chalk.cyan.bold('\n Kanal Durumu\n'));
95
+
96
+ const checks = [
97
+ { name: 'Telegram', ok: !!config.telegramToken },
98
+ { name: 'WhatsApp', ok: !!config.whatsappConnected },
99
+ { name: 'Discord', ok: !!config.discordToken },
100
+ { name: 'Slack', ok: !!config.slackToken },
101
+ ];
102
+
103
+ checks.forEach(ch => {
104
+ const icon = ch.ok ? chalk.green('✓') : chalk.gray('○');
105
+ const label = ch.ok ? chalk.white(ch.name) : chalk.gray(ch.name);
106
+ const status = ch.ok ? chalk.green('bağlı') : chalk.gray('bağlı değil');
107
+ console.log(` ${icon} ${label.padEnd(12)} ${status}`);
108
+ });
109
+
110
+ const connected = checks.filter(c => c.ok).length;
111
+ console.log('');
112
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
113
+ console.log(chalk.gray(` ${connected}/4 kanal aktif\n`));
114
+ }
115
+
116
+ function removeChannel(channel) {
117
+ if (!channel) {
118
+ console.log(chalk.red('\n ❌ Kanal adı gerekli\n'));
119
+ console.log(chalk.gray(' Kullanım: natureco channels remove <telegram|whatsapp|discord|slack>\n'));
120
+ process.exit(1);
121
+ }
122
+
123
+ const config = getConfig();
124
+ const ch = channel.toLowerCase();
125
+
126
+ if (ch === 'telegram') {
127
+ delete config.telegramToken;
128
+ delete config.telegramBotId;
129
+ delete config.telegramAllowedChats;
130
+ } else if (ch === 'whatsapp') {
131
+ // Session dosyalarını sil
132
+ if (config.whatsappBotId) {
133
+ const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
134
+ if (fs.existsSync(sessionDir)) {
135
+ fs.rmSync(sessionDir, { recursive: true, force: true });
136
+ }
137
+ }
138
+ delete config.whatsappConnected;
139
+ delete config.whatsappBotId;
140
+ delete config.whatsappPhone;
141
+ delete config.whatsappAllowedNumbers;
142
+ } else if (ch === 'discord') {
143
+ delete config.discordToken;
144
+ delete config.discordBotId;
145
+ } else if (ch === 'slack') {
146
+ delete config.slackToken;
147
+ delete config.slackBotId;
148
+ } else {
149
+ console.log(chalk.red(`\n ❌ Bilinmeyen kanal: ${channel}\n`));
150
+ process.exit(1);
151
+ }
152
+
153
+ saveConfig(config);
154
+ console.log(chalk.green(`\n ✓ ${channel} kaldırıldı\n`));
155
+ }
156
+
157
+ function channelLogs(channel) {
158
+ const logFile = path.join(os.homedir(), '.natureco', 'gateway.log');
159
+ if (!fs.existsSync(logFile)) {
160
+ console.log(chalk.gray('\n Log dosyası bulunamadı.\n'));
161
+ return;
162
+ }
163
+
164
+ const lines = fs.readFileSync(logFile, 'utf-8').split('\n').filter(l => l.trim());
165
+ const filtered = channel
166
+ ? lines.filter(l => l.toLowerCase().includes(channel.toLowerCase()))
167
+ : lines;
168
+
169
+ const last = filtered.slice(-20);
170
+ console.log('');
171
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
172
+ console.log(chalk.cyan.bold(`\n Kanal Logları${channel ? ` (${channel})` : ''}\n`));
173
+ last.forEach(l => console.log(chalk.gray(' ' + l)));
174
+ console.log('');
175
+ }
176
+
177
+ module.exports = channels;
@@ -211,7 +211,7 @@ body::before{
211
211
  <div class="header-bot-name" id="header-bot-name">Nature Bot</div>
212
212
  <div class="header-bot-model" id="header-bot-model">NatureCo</div>
213
213
  </div>
214
- <div class="version-badge" id="version-badge">v2.16.5</div>
214
+ <div class="version-badge" id="version-badge">v2.17.1</div>
215
215
  </div>
216
216
  <div class="messages" id="messages"></div>
217
217
  <div class="input-area">
@@ -341,7 +341,7 @@ function dashboard(action) {
341
341
  apiKey: cfg.apiKey,
342
342
  defaultBot: cfg.defaultBot,
343
343
  defaultBotId: cfg.defaultBotId,
344
- version: 'v2.16.5',
344
+ version: 'v2.17.1',
345
345
  bots: cfg.bots || [],
346
346
  telegramToken: cfg.telegramToken || null,
347
347
  whatsappConnected: cfg.whatsappConnected || false,
@@ -200,13 +200,10 @@ async function gitExplain() {
200
200
  }
201
201
 
202
202
  async function getDefaultBot() {
203
- const apiKey = getApiKey();
204
-
205
- if (!apiKey) {
206
- console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
207
- process.exit(1);
208
- }
209
-
203
+ const { getConfig } = require('../utils/config');
204
+ const config = getConfig();
205
+ const apiKey = getApiKey() || config.providerApiKey || '';
206
+
210
207
  let botList;
211
208
  try {
212
209
  botList = await getBots(apiKey);
@@ -214,15 +211,15 @@ async function getDefaultBot() {
214
211
  console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
215
212
  process.exit(1);
216
213
  }
217
-
214
+
218
215
  if (!botList || !botList.bots || botList.bots.length === 0) {
219
216
  console.log(chalk.gray('No bots found. Create one at https://developers.natureco.me\n'));
220
217
  process.exit(1);
221
218
  }
222
-
223
- // Use first bot as default
224
- const bot = botList.bots[0];
225
-
219
+
220
+ // Config'deki botName ile eşleştir, yoksa ilk botu kullan
221
+ const bot = botList.bots.find(b => b.name === config.botName) || botList.bots[0];
222
+
226
223
  return { apiKey, bot };
227
224
  }
228
225
 
@@ -72,11 +72,18 @@ function help() {
72
72
 
73
73
  // ── Diğer ────────────────────────────────────────────────────
74
74
  console.log(chalk.cyan.bold('\n Diğer\n'));
75
- printCmd('natureco sessions list', 'Geçmiş oturumları listele');
76
- printCmd('natureco tasks list', 'Arka plan görevlerini listele');
77
- printCmd('natureco config list', 'Tüm ayarları göster');
78
- printCmd('natureco config set <k> <v>', 'Ayar değiştir');
79
- printCmd('natureco init', 'Proje klasörü oluştur (.natureco/)');
75
+ printCmd('natureco channels [action]', 'Kanal yönetimi (list|status|remove|logs)');
76
+ printCmd('natureco models [action]', 'Model yönetimi (list|set|scan|aliases)');
77
+ printCmd('natureco memory [action]', 'Hafıza yönetimi (status|list|search|show|clear)');
78
+ printCmd('natureco logs', 'Gateway loglarını göster (--follow ile canlı)');
79
+ printCmd('natureco status', 'Genel sistem durumu');
80
+ printCmd('natureco security audit', 'Güvenlik denetimi (--fix ile otomatik düzelt)');
81
+ printCmd('natureco reset', 'Config sıfırla (--scope config|memory|sessions|full)');
82
+ printCmd('natureco sessions list', 'Geçmiş oturumları listele');
83
+ printCmd('natureco tasks list', 'Arka plan görevlerini listele');
84
+ printCmd('natureco config list', 'Tüm ayarları göster');
85
+ printCmd('natureco config set <k> <v>', 'Ayar değiştir');
86
+ printCmd('natureco init', 'Proje klasörü oluştur (.natureco/)');
80
87
 
81
88
  // ── Chat içi komutlar ────────────────────────────────────────
82
89
  console.log(chalk.cyan.bold('\n Chat İçi Komutlar\n'));
@@ -0,0 +1,99 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ const LOG_FILE = path.join(os.homedir(), '.natureco', 'gateway.log');
7
+
8
+ async function logs(args) {
9
+ const follow = args.includes('--follow') || args.includes('-f');
10
+ const json = args.includes('--json');
11
+ const plain = args.includes('--plain');
12
+ const limitIdx = args.indexOf('--limit');
13
+ const limit = limitIdx !== -1 && args[limitIdx + 1]
14
+ ? parseInt(args[limitIdx + 1]) || 50
15
+ : (args.find(a => a.startsWith('--limit='))
16
+ ? parseInt(args.find(a => a.startsWith('--limit=')).split('=')[1]) || 50
17
+ : 50);
18
+
19
+ if (!fs.existsSync(LOG_FILE)) {
20
+ if (!json) console.log(chalk.gray('\n Log dosyası bulunamadı: ' + LOG_FILE + '\n'));
21
+ else console.log(JSON.stringify({ error: 'Log file not found' }));
22
+ return;
23
+ }
24
+
25
+ const printLines = (lines) => {
26
+ const last = lines.slice(-limit);
27
+ if (json) {
28
+ console.log(JSON.stringify({ lines: last }));
29
+ return;
30
+ }
31
+ if (!plain) {
32
+ console.log('');
33
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
34
+ console.log(chalk.cyan.bold(`\n Gateway Logları (son ${limit})\n`));
35
+ }
36
+ last.forEach(line => {
37
+ if (!line.trim()) return;
38
+ if (plain) {
39
+ console.log(line);
40
+ return;
41
+ }
42
+ // Renklendirme
43
+ if (line.includes('[error]') || line.includes('ERROR') || line.includes('❌')) {
44
+ console.log(chalk.red(' ' + line));
45
+ } else if (line.includes('[warn]') || line.includes('WARN') || line.includes('⚠')) {
46
+ console.log(chalk.yellow(' ' + line));
47
+ } else if (line.includes('[whatsapp]') || line.includes('[telegram]') || line.includes('[discord]')) {
48
+ console.log(chalk.cyan(' ' + line));
49
+ } else {
50
+ console.log(chalk.gray(' ' + line));
51
+ }
52
+ });
53
+ if (!plain) console.log('');
54
+ };
55
+
56
+ const content = fs.readFileSync(LOG_FILE, 'utf-8');
57
+ const lines = content.split('\n').filter(l => l.trim());
58
+ printLines(lines);
59
+
60
+ if (follow) {
61
+ if (!plain) console.log(chalk.gray(' Canlı takip modunda... (Ctrl+C ile çık)\n'));
62
+ let lastSize = fs.statSync(LOG_FILE).size;
63
+
64
+ const watcher = setInterval(() => {
65
+ try {
66
+ const stat = fs.statSync(LOG_FILE);
67
+ if (stat.size > lastSize) {
68
+ const fd = fs.openSync(LOG_FILE, 'r');
69
+ const buf = Buffer.alloc(stat.size - lastSize);
70
+ fs.readSync(fd, buf, 0, buf.length, lastSize);
71
+ fs.closeSync(fd);
72
+ const newLines = buf.toString('utf-8').split('\n').filter(l => l.trim());
73
+ newLines.forEach(line => {
74
+ if (!line.trim()) return;
75
+ if (plain) { console.log(line); return; }
76
+ if (line.includes('[error]') || line.includes('ERROR')) {
77
+ console.log(chalk.red(' ' + line));
78
+ } else if (line.includes('[warn]') || line.includes('WARN')) {
79
+ console.log(chalk.yellow(' ' + line));
80
+ } else if (line.includes('[whatsapp]') || line.includes('[telegram]')) {
81
+ console.log(chalk.cyan(' ' + line));
82
+ } else {
83
+ console.log(chalk.gray(' ' + line));
84
+ }
85
+ });
86
+ lastSize = stat.size;
87
+ }
88
+ } catch {}
89
+ }, 500);
90
+
91
+ process.on('SIGINT', () => {
92
+ clearInterval(watcher);
93
+ console.log(chalk.gray('\n Takip durduruldu.\n'));
94
+ process.exit(0);
95
+ });
96
+ }
97
+ }
98
+
99
+ module.exports = logs;
@@ -0,0 +1,190 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { loadMemory, saveMemory, clearMemory } = require('../utils/memory');
6
+ const { getConfig } = require('../utils/config');
7
+
8
+ const MEMORY_DIR = path.join(os.homedir(), '.natureco', 'memory');
9
+
10
+ async function memoryCmd(args) {
11
+ const [action, ...params] = (args || []);
12
+
13
+ if (!action || action === 'status') return statusMemory();
14
+ if (action === 'list') return listMemory();
15
+ if (action === 'search') return searchMemory(params.join(' '));
16
+ if (action === 'show') return showMemory(params[0]);
17
+ if (action === 'clear') return clearMemoryCmd(params[0]);
18
+ if (action === 'index') return indexMemory();
19
+
20
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
21
+ console.log(chalk.gray(' Kullanım: natureco memory [status|list|search|show|clear|index]\n'));
22
+ process.exit(1);
23
+ }
24
+
25
+ function statusMemory() {
26
+ const config = getConfig();
27
+
28
+ console.log('');
29
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
30
+ console.log(chalk.cyan.bold('\n Hafıza Durumu\n'));
31
+
32
+ if (!fs.existsSync(MEMORY_DIR)) {
33
+ console.log(chalk.gray(' Hafıza dizini bulunamadı.\n'));
34
+ return;
35
+ }
36
+
37
+ const files = fs.readdirSync(MEMORY_DIR).filter(f => f.endsWith('.json'));
38
+
39
+ if (files.length === 0) {
40
+ console.log(chalk.gray(' Kayıtlı hafıza yok.\n'));
41
+ return;
42
+ }
43
+
44
+ let totalFacts = 0;
45
+ files.forEach(file => {
46
+ const botId = file.replace('.json', '');
47
+ try {
48
+ const mem = loadMemory(botId);
49
+ const factCount = (mem.facts || []).length;
50
+ totalFacts += factCount;
51
+ const active = config.botName && (mem.botName === config.botName) ? chalk.green(' ← aktif') : '';
52
+ console.log(chalk.white(` ${mem.botName || botId}`) + active);
53
+ if (mem.name) console.log(chalk.gray(` Kullanıcı : ${mem.name}`));
54
+ if (mem.nickname) console.log(chalk.gray(` Lakap : ${mem.nickname}`));
55
+ console.log(chalk.gray(` Bilgi : ${factCount} kayıt`));
56
+ if (mem.lastSeen) {
57
+ const d = new Date(mem.lastSeen);
58
+ console.log(chalk.gray(` Son görüş : ${d.toLocaleDateString('tr-TR')}`));
59
+ }
60
+ console.log('');
61
+ } catch {}
62
+ });
63
+
64
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
65
+ console.log(chalk.gray(` ${files.length} bot · ${totalFacts} toplam bilgi`));
66
+ console.log(chalk.gray(' Aramak için: ') + chalk.cyan('natureco memory search <sorgu>\n'));
67
+ }
68
+
69
+ function listMemory() {
70
+ if (!fs.existsSync(MEMORY_DIR)) {
71
+ console.log(chalk.gray('\n Hafıza dizini bulunamadı.\n'));
72
+ return;
73
+ }
74
+
75
+ const files = fs.readdirSync(MEMORY_DIR).filter(f => f.endsWith('.json'));
76
+ console.log(chalk.cyan.bold(`\n Hafıza Dosyaları (${files.length})\n`));
77
+ files.forEach(f => {
78
+ const botId = f.replace('.json', '');
79
+ const mem = loadMemory(botId);
80
+ console.log(chalk.white(` ${f}`));
81
+ console.log(chalk.gray(` Bot: ${mem.botName || botId} · ${(mem.facts || []).length} bilgi`));
82
+ });
83
+ console.log('');
84
+ }
85
+
86
+ function searchMemory(query) {
87
+ if (!query) {
88
+ console.log(chalk.red('\n ❌ Arama sorgusu gerekli\n'));
89
+ console.log(chalk.gray(' Kullanım: natureco memory search <sorgu>\n'));
90
+ process.exit(1);
91
+ }
92
+
93
+ if (!fs.existsSync(MEMORY_DIR)) {
94
+ console.log(chalk.gray('\n Hafıza dizini bulunamadı.\n'));
95
+ return;
96
+ }
97
+
98
+ const files = fs.readdirSync(MEMORY_DIR).filter(f => f.endsWith('.json'));
99
+ const results = [];
100
+
101
+ files.forEach(file => {
102
+ const botId = file.replace('.json', '');
103
+ const mem = loadMemory(botId);
104
+ const q = query.toLowerCase();
105
+
106
+ if (mem.name?.toLowerCase().includes(q)) {
107
+ results.push({ bot: mem.botName || botId, field: 'İsim', value: mem.name });
108
+ }
109
+ if (mem.nickname?.toLowerCase().includes(q)) {
110
+ results.push({ bot: mem.botName || botId, field: 'Lakap', value: mem.nickname });
111
+ }
112
+ (mem.facts || []).forEach(f => {
113
+ const val = typeof f === 'string' ? f : f.value;
114
+ if (val?.toLowerCase().includes(q)) {
115
+ results.push({ bot: mem.botName || botId, field: 'Bilgi', value: val });
116
+ }
117
+ });
118
+ });
119
+
120
+ console.log(chalk.cyan.bold(`\n "${query}" için ${results.length} sonuç\n`));
121
+ if (results.length === 0) {
122
+ console.log(chalk.gray(' Sonuç bulunamadı.\n'));
123
+ return;
124
+ }
125
+ results.forEach(r => {
126
+ console.log(chalk.white(` [${r.bot}] `) + chalk.gray(`${r.field}: `) + chalk.white(r.value));
127
+ });
128
+ console.log('');
129
+ }
130
+
131
+ function showMemory(botId) {
132
+ const config = getConfig();
133
+ const id = botId || 'universal-provider';
134
+ const mem = loadMemory(id);
135
+
136
+ console.log('');
137
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
138
+ console.log(chalk.cyan.bold(`\n Hafıza: ${mem.botName || id}\n`));
139
+
140
+ if (mem.name) console.log(chalk.gray(' İsim : ') + chalk.white(mem.name));
141
+ if (mem.nickname) console.log(chalk.gray(' Lakap : ') + chalk.white(mem.nickname));
142
+ if (mem.botName) console.log(chalk.gray(' Bot adı : ') + chalk.cyan(mem.botName));
143
+
144
+ const facts = mem.facts || [];
145
+ if (facts.length > 0) {
146
+ console.log(chalk.gray('\n Bilgiler:\n'));
147
+ facts.forEach((f, i) => {
148
+ const val = typeof f === 'string' ? f : f.value;
149
+ const score = typeof f === 'object' ? f.score : 5;
150
+ console.log(chalk.gray(` ${(i + 1).toString().padStart(2)}. `) + chalk.white(val) + chalk.gray(` (${score})`));
151
+ });
152
+ }
153
+ console.log('');
154
+ }
155
+
156
+ function clearMemoryCmd(botId) {
157
+ const id = botId || 'universal-provider';
158
+ clearMemory(id);
159
+ console.log(chalk.green(`\n ✓ Hafıza temizlendi: ${id}\n`));
160
+ }
161
+
162
+ function indexMemory() {
163
+ if (!fs.existsSync(MEMORY_DIR)) {
164
+ console.log(chalk.gray('\n Hafıza dizini bulunamadı.\n'));
165
+ return;
166
+ }
167
+
168
+ const files = fs.readdirSync(MEMORY_DIR).filter(f => f.endsWith('.json'));
169
+ let indexed = 0;
170
+
171
+ files.forEach(file => {
172
+ const botId = file.replace('.json', '');
173
+ try {
174
+ const mem = loadMemory(botId);
175
+ // Bozuk fact'leri temizle
176
+ if (Array.isArray(mem.facts)) {
177
+ mem.facts = mem.facts.filter(f => {
178
+ const val = typeof f === 'string' ? f : f?.value;
179
+ return val && typeof val === 'string' && val.length > 0;
180
+ });
181
+ saveMemory(botId, mem);
182
+ indexed++;
183
+ }
184
+ } catch {}
185
+ });
186
+
187
+ console.log(chalk.green(`\n ✓ ${indexed} hafıza dosyası yeniden indexlendi\n`));
188
+ }
189
+
190
+ module.exports = memoryCmd;
@@ -0,0 +1,216 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const { getConfig, saveConfig } = require('../utils/config');
4
+
5
+ const PROVIDER_MODELS = {
6
+ 'api.groq.com': [
7
+ { id: 'llama-3.3-70b-versatile', label: 'Llama 3.3 70B Versatile (önerilen)' },
8
+ { id: 'llama-3.1-8b-instant', label: 'Llama 3.1 8B Instant (hızlı)' },
9
+ { id: 'llama-3.1-70b-versatile', label: 'Llama 3.1 70B Versatile' },
10
+ { id: 'mixtral-8x7b-32768', label: 'Mixtral 8x7B' },
11
+ { id: 'gemma2-9b-it', label: 'Gemma 2 9B' },
12
+ ],
13
+ 'api.openai.com': [
14
+ { id: 'gpt-4o', label: 'GPT-4o (önerilen)' },
15
+ { id: 'gpt-4o-mini', label: 'GPT-4o Mini (hızlı)' },
16
+ { id: 'gpt-4-turbo', label: 'GPT-4 Turbo' },
17
+ { id: 'gpt-3.5-turbo',label: 'GPT-3.5 Turbo (ucuz)' },
18
+ ],
19
+ 'api.anthropic.com': [
20
+ { id: 'claude-opus-4-7', label: 'Claude Opus 4.7 (en güçlü)' },
21
+ { id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6 (önerilen)' },
22
+ { id: 'claude-haiku-4-5-20251001',label: 'Claude Haiku 4.5 (hızlı)' },
23
+ ],
24
+ 'api.together.xyz': [
25
+ { id: 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo', label: 'Llama 3.1 70B Turbo' },
26
+ { id: 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', label: 'Llama 3.1 8B Turbo' },
27
+ { id: 'mistralai/Mixtral-8x7B-Instruct-v0.1', label: 'Mixtral 8x7B' },
28
+ ],
29
+ 'natureco.me': [
30
+ { id: 'natureco-default', label: 'NatureCo Default (otomatik)' },
31
+ ],
32
+ };
33
+
34
+ async function models(args) {
35
+ const [action, ...params] = (args || []);
36
+
37
+ if (!action || action === 'list' || action === 'status') return listModels();
38
+ if (action === 'set') return setModel(params[0]);
39
+ if (action === 'scan') return scanModels();
40
+ if (action === 'aliases') return manageAliases(params);
41
+
42
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
43
+ console.log(chalk.gray(' Kullanım: natureco models [list|set|scan|aliases]\n'));
44
+ process.exit(1);
45
+ }
46
+
47
+ function listModels() {
48
+ const config = getConfig();
49
+ const providerUrl = config.providerUrl || '';
50
+ const currentModel = config.providerModel || 'bilinmiyor';
51
+
52
+ // Provider'a göre model listesi bul
53
+ let modelList = [];
54
+ for (const [domain, list] of Object.entries(PROVIDER_MODELS)) {
55
+ if (providerUrl.includes(domain)) {
56
+ modelList = list;
57
+ break;
58
+ }
59
+ }
60
+
61
+ const providerHost = providerUrl.replace('https://', '').split('/')[0] || 'yapılandırılmamış';
62
+
63
+ console.log('');
64
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
65
+ console.log(chalk.cyan.bold('\n Model Yapılandırması\n'));
66
+ console.log(chalk.gray(' Provider : ') + chalk.white(providerHost));
67
+ console.log(chalk.gray(' Aktif : ') + chalk.cyan(currentModel));
68
+
69
+ if (config.modelAliases && Object.keys(config.modelAliases).length > 0) {
70
+ console.log(chalk.gray(' Takma ad : ') + chalk.white(
71
+ Object.entries(config.modelAliases).map(([k, v]) => `${k}→${v}`).join(', ')
72
+ ));
73
+ }
74
+
75
+ if (modelList.length > 0) {
76
+ console.log(chalk.cyan.bold('\n Mevcut Modeller\n'));
77
+ modelList.forEach(m => {
78
+ const active = m.id === currentModel ? chalk.green(' ← aktif') : '';
79
+ console.log(chalk.white(` ${m.id}`) + active);
80
+ console.log(chalk.gray(` ${m.label}`));
81
+ });
82
+ }
83
+
84
+ console.log('');
85
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
86
+ console.log(chalk.gray(' Değiştirmek için: ') + chalk.cyan('natureco models set <model-id>'));
87
+ console.log(chalk.gray(' Taramak için: ') + chalk.cyan('natureco models scan\n'));
88
+ }
89
+
90
+ async function setModel(modelId) {
91
+ const config = getConfig();
92
+ const providerUrl = config.providerUrl || '';
93
+
94
+ if (!modelId) {
95
+ // İnteraktif seçim
96
+ let modelList = [];
97
+ for (const [domain, list] of Object.entries(PROVIDER_MODELS)) {
98
+ if (providerUrl.includes(domain)) {
99
+ modelList = list;
100
+ break;
101
+ }
102
+ }
103
+
104
+ if (modelList.length === 0) {
105
+ const { customModel } = await inquirer.prompt([{
106
+ type: 'input',
107
+ name: 'customModel',
108
+ message: ' Model ID girin:',
109
+ validate: v => v.trim() ? true : 'Model ID gerekli',
110
+ }]);
111
+ modelId = customModel.trim();
112
+ } else {
113
+ const { selected } = await inquirer.prompt([{
114
+ type: 'list',
115
+ name: 'selected',
116
+ message: ' Model seç:',
117
+ choices: modelList.map(m => ({ name: m.label, value: m.id })),
118
+ }]);
119
+ modelId = selected;
120
+ }
121
+ }
122
+
123
+ config.providerModel = modelId;
124
+ saveConfig(config);
125
+
126
+ console.log(chalk.green(`\n ✓ Model güncellendi: ${modelId}\n`));
127
+ }
128
+
129
+ async function scanModels() {
130
+ const config = getConfig();
131
+ const providerUrl = config.providerUrl || '';
132
+
133
+ console.log(chalk.gray('\n Modeller taranıyor...\n'));
134
+
135
+ // OpenRouter ücretsiz modelleri
136
+ if (providerUrl.includes('openrouter')) {
137
+ try {
138
+ const res = await fetch('https://openrouter.ai/api/v1/models', {
139
+ headers: { 'Authorization': `Bearer ${config.providerApiKey}` }
140
+ });
141
+ if (res.ok) {
142
+ const data = await res.json();
143
+ const free = (data.data || []).filter(m => m.pricing?.prompt === '0');
144
+ console.log(chalk.cyan.bold(` OpenRouter Ücretsiz Modeller (${free.length})\n`));
145
+ free.slice(0, 15).forEach(m => {
146
+ console.log(chalk.white(` ${m.id}`));
147
+ console.log(chalk.gray(` ${(m.description || '').slice(0, 60)}`));
148
+ });
149
+ console.log('');
150
+ return;
151
+ }
152
+ } catch {}
153
+ }
154
+
155
+ // Provider'a göre bilinen modelleri göster
156
+ let modelList = [];
157
+ for (const [domain, list] of Object.entries(PROVIDER_MODELS)) {
158
+ if (providerUrl.includes(domain)) {
159
+ modelList = list;
160
+ break;
161
+ }
162
+ }
163
+
164
+ if (modelList.length > 0) {
165
+ console.log(chalk.cyan.bold(` Bilinen Modeller\n`));
166
+ modelList.forEach(m => {
167
+ console.log(chalk.white(` ${m.id}`));
168
+ console.log(chalk.gray(` ${m.label}`));
169
+ });
170
+ } else {
171
+ console.log(chalk.gray(' Bu provider için model listesi bilinmiyor.'));
172
+ console.log(chalk.gray(' Manuel ayarlamak için: ') + chalk.cyan('natureco models set <model-id>\n'));
173
+ }
174
+ console.log('');
175
+ }
176
+
177
+ function manageAliases(params) {
178
+ const config = getConfig();
179
+ const aliases = config.modelAliases || {};
180
+
181
+ if (params.length === 0) {
182
+ // Listele
183
+ console.log(chalk.cyan.bold('\n Model Takma Adları\n'));
184
+ if (Object.keys(aliases).length === 0) {
185
+ console.log(chalk.gray(' Takma ad yok.\n'));
186
+ console.log(chalk.gray(' Eklemek için: ') + chalk.cyan('natureco models aliases <takma-ad> <model-id>\n'));
187
+ return;
188
+ }
189
+ Object.entries(aliases).forEach(([alias, model]) => {
190
+ console.log(chalk.white(` ${alias.padEnd(15)} → `) + chalk.cyan(model));
191
+ });
192
+ console.log('');
193
+ return;
194
+ }
195
+
196
+ if (params.length === 2) {
197
+ const [alias, modelId] = params;
198
+ aliases[alias] = modelId;
199
+ config.modelAliases = aliases;
200
+ saveConfig(config);
201
+ console.log(chalk.green(`\n ✓ Takma ad eklendi: ${alias} → ${modelId}\n`));
202
+ return;
203
+ }
204
+
205
+ if (params.length === 1 && aliases[params[0]]) {
206
+ delete aliases[params[0]];
207
+ config.modelAliases = aliases;
208
+ saveConfig(config);
209
+ console.log(chalk.green(`\n ✓ Takma ad silindi: ${params[0]}\n`));
210
+ return;
211
+ }
212
+
213
+ console.log(chalk.gray('\n Kullanım: natureco models aliases [<takma-ad> <model-id>]\n'));
214
+ }
215
+
216
+ module.exports = models;
@@ -0,0 +1,94 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const { CONFIG_DIR, CONFIG_FILE } = require('../utils/config');
7
+
8
+ async function reset(args) {
9
+ const scope = args?.find(a => a.startsWith('--scope='))?.split('=')[1]
10
+ || (args?.includes('--scope') ? args[args.indexOf('--scope') + 1] : null)
11
+ || 'config';
12
+ const yes = args?.includes('--yes') || args?.includes('-y');
13
+
14
+ console.log('');
15
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
16
+ console.log(chalk.yellow.bold('\n NatureCo CLI — Sıfırlama\n'));
17
+
18
+ const scopeDescriptions = {
19
+ config: 'Sadece config dosyasını sıfırla (~/.natureco/config.json)',
20
+ memory: 'Tüm hafıza dosyalarını sil (~/.natureco/memory/)',
21
+ sessions: 'Tüm session dosyalarını sil (~/.natureco/sessions/)',
22
+ full: 'Her şeyi sil (~/.natureco/ klasörü tamamen)',
23
+ };
24
+
25
+ console.log(chalk.gray(` Kapsam: `) + chalk.white(scope));
26
+ console.log(chalk.gray(` İşlem: `) + chalk.yellow(scopeDescriptions[scope] || scopeDescriptions.config));
27
+ console.log('');
28
+
29
+ if (!yes) {
30
+ const { confirm } = await inquirer.prompt([{
31
+ type: 'confirm',
32
+ name: 'confirm',
33
+ message: ' Devam etmek istediğinizden emin misiniz?',
34
+ default: false,
35
+ }]);
36
+ if (!confirm) {
37
+ console.log(chalk.gray('\n İptal edildi.\n'));
38
+ return;
39
+ }
40
+ }
41
+
42
+ if (scope === 'config' || scope === 'full') {
43
+ if (fs.existsSync(CONFIG_FILE)) {
44
+ fs.unlinkSync(CONFIG_FILE);
45
+ console.log(chalk.green(' ✓ Config dosyası silindi'));
46
+ }
47
+ }
48
+
49
+ if (scope === 'memory' || scope === 'full') {
50
+ const memDir = path.join(CONFIG_DIR, 'memory');
51
+ if (fs.existsSync(memDir)) {
52
+ fs.rmSync(memDir, { recursive: true, force: true });
53
+ console.log(chalk.green(' ✓ Hafıza dosyaları silindi'));
54
+ }
55
+ }
56
+
57
+ if (scope === 'sessions' || scope === 'full') {
58
+ const sessDir = path.join(CONFIG_DIR, 'sessions');
59
+ if (fs.existsSync(sessDir)) {
60
+ fs.rmSync(sessDir, { recursive: true, force: true });
61
+ console.log(chalk.green(' ✓ Session dosyaları silindi'));
62
+ }
63
+ const convDir = path.join(CONFIG_DIR, 'conversations');
64
+ if (fs.existsSync(convDir)) {
65
+ fs.rmSync(convDir, { recursive: true, force: true });
66
+ console.log(chalk.green(' ✓ Konuşma geçmişi silindi'));
67
+ }
68
+ }
69
+
70
+ if (scope === 'full') {
71
+ // WhatsApp sessions
72
+ const waDir = path.join(CONFIG_DIR, 'whatsapp-sessions');
73
+ if (fs.existsSync(waDir)) {
74
+ fs.rmSync(waDir, { recursive: true, force: true });
75
+ console.log(chalk.green(' ✓ WhatsApp session\'ları silindi'));
76
+ }
77
+ // Skills
78
+ const skillsDir = path.join(CONFIG_DIR, 'skills');
79
+ if (fs.existsSync(skillsDir)) {
80
+ fs.rmSync(skillsDir, { recursive: true, force: true });
81
+ console.log(chalk.green(' ✓ Skill\'ler silindi'));
82
+ }
83
+ }
84
+
85
+ console.log('');
86
+ console.log(chalk.green(' ✓ Sıfırlama tamamlandı!'));
87
+ if (scope === 'config' || scope === 'full') {
88
+ console.log(chalk.gray('\n Yeniden kurulum için: ') + chalk.cyan('natureco setup\n'));
89
+ } else {
90
+ console.log('');
91
+ }
92
+ }
93
+
94
+ module.exports = reset;
@@ -0,0 +1,142 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { getConfig, CONFIG_FILE, CONFIG_DIR } = require('../utils/config');
6
+
7
+ async function security(args) {
8
+ const [action, ...params] = (args || []);
9
+
10
+ if (!action || action === 'audit') return audit(args);
11
+
12
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
13
+ console.log(chalk.gray(' Kullanım: natureco security [audit]\n'));
14
+ process.exit(1);
15
+ }
16
+
17
+ async function audit(args) {
18
+ const fix = args.includes('--fix');
19
+ const json = args.includes('--json');
20
+
21
+ const issues = [];
22
+ const warnings = [];
23
+ const ok = [];
24
+
25
+ // 1. Config dosyası izinleri
26
+ if (fs.existsSync(CONFIG_FILE)) {
27
+ try {
28
+ const stat = fs.statSync(CONFIG_FILE);
29
+ const mode = (stat.mode & 0o777).toString(8);
30
+ if (process.platform !== 'win32' && mode !== '600' && mode !== '644') {
31
+ issues.push({
32
+ id: 'config-permissions',
33
+ msg: `Config dosyası izinleri geniş: ${mode} (önerilen: 600)`,
34
+ fix: () => fs.chmodSync(CONFIG_FILE, 0o600),
35
+ });
36
+ } else {
37
+ ok.push('Config dosyası izinleri');
38
+ }
39
+ } catch {}
40
+ }
41
+
42
+ // 2. API key formatı
43
+ const config = getConfig();
44
+ if (config.providerApiKey) {
45
+ if (config.providerApiKey.length < 10) {
46
+ issues.push({ id: 'weak-api-key', msg: 'API key çok kısa görünüyor' });
47
+ } else {
48
+ ok.push('API key formatı');
49
+ }
50
+ }
51
+
52
+ // 3. Debug mode
53
+ if (config.debug === true) {
54
+ warnings.push({ id: 'debug-mode', msg: 'Debug modu açık — API key\'ler loglara yazılabilir' });
55
+ } else {
56
+ ok.push('Debug modu kapalı');
57
+ }
58
+
59
+ // 4. WhatsApp session dosyaları
60
+ const waDir = path.join(CONFIG_DIR, 'whatsapp-sessions');
61
+ if (fs.existsSync(waDir)) {
62
+ const sessions = fs.readdirSync(waDir);
63
+ if (sessions.length > 3) {
64
+ warnings.push({ id: 'old-wa-sessions', msg: `${sessions.length} WhatsApp session var — eskiler temizlenebilir` });
65
+ } else {
66
+ ok.push('WhatsApp session sayısı');
67
+ }
68
+ }
69
+
70
+ // 5. Gateway log boyutu
71
+ const logFile = path.join(CONFIG_DIR, 'gateway.log');
72
+ if (fs.existsSync(logFile)) {
73
+ const stat = fs.statSync(logFile);
74
+ const sizeMB = stat.size / (1024 * 1024);
75
+ if (sizeMB > 50) {
76
+ warnings.push({ id: 'large-log', msg: `Gateway log dosyası büyük: ${sizeMB.toFixed(1)}MB` });
77
+ } else {
78
+ ok.push('Gateway log boyutu');
79
+ }
80
+ }
81
+
82
+ // 6. Eski conversation dosyaları
83
+ const convDir = path.join(CONFIG_DIR, 'conversations');
84
+ if (fs.existsSync(convDir)) {
85
+ const convFiles = fs.readdirSync(convDir).filter(f => f.endsWith('.json'));
86
+ if (convFiles.length > 20) {
87
+ warnings.push({ id: 'old-conversations', msg: `${convFiles.length} konuşma dosyası var — eskiler temizlenebilir` });
88
+ } else {
89
+ ok.push('Konuşma dosyası sayısı');
90
+ }
91
+ }
92
+
93
+ if (json) {
94
+ console.log(JSON.stringify({ ok, warnings: warnings.map(w => w.msg), issues: issues.map(i => i.msg) }, null, 2));
95
+ return;
96
+ }
97
+
98
+ console.log('');
99
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
100
+ console.log(chalk.cyan.bold('\n Güvenlik Denetimi\n'));
101
+
102
+ ok.forEach(msg => console.log(chalk.green(' ✓ ') + chalk.gray(msg)));
103
+
104
+ if (warnings.length > 0) {
105
+ console.log('');
106
+ warnings.forEach(w => console.log(chalk.yellow(' ⚠ ') + chalk.yellow(w.msg)));
107
+ }
108
+
109
+ if (issues.length > 0) {
110
+ console.log('');
111
+ issues.forEach(i => console.log(chalk.red(' ❌ ') + chalk.red(i.msg)));
112
+ }
113
+
114
+ console.log('');
115
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
116
+
117
+ if (issues.length === 0 && warnings.length === 0) {
118
+ console.log(chalk.green(' ✓ Güvenlik sorunu bulunamadı\n'));
119
+ return;
120
+ }
121
+
122
+ console.log(chalk.gray(` ${ok.length} geçti · ${warnings.length} uyarı · ${issues.length} sorun`));
123
+
124
+ if (fix && issues.length > 0) {
125
+ console.log('');
126
+ issues.forEach(i => {
127
+ if (i.fix) {
128
+ try {
129
+ i.fix();
130
+ console.log(chalk.green(` ✓ Düzeltildi: ${i.id}`));
131
+ } catch (e) {
132
+ console.log(chalk.red(` ❌ Düzeltilemedi: ${i.id} — ${e.message}`));
133
+ }
134
+ }
135
+ });
136
+ } else if (issues.length > 0) {
137
+ console.log(chalk.gray('\n Otomatik düzeltmek için: ') + chalk.cyan('natureco security audit --fix'));
138
+ }
139
+ console.log('');
140
+ }
141
+
142
+ module.exports = security;
@@ -68,37 +68,33 @@ async function skills(args) {
68
68
  }
69
69
 
70
70
  async function listSkills() {
71
- console.log(chalk.green.bold('\n╭─ Yüklü Skill\'ler ─╮\n'));
72
-
73
71
  const allSkills = getSkills();
74
72
 
73
+ console.log(chalk.gray('\n ' + '─'.repeat(48)));
74
+ console.log(chalk.cyan.bold('\n Yüklü Skill\'ler\n'));
75
+
75
76
  if (allSkills.length === 0) {
76
- console.log(chalk.gray('Hiç skill yüklü değil.\n'));
77
- console.log(chalk.yellow('Skill yüklemek için:'), chalk.white('natureco skills install <slug>\n'));
77
+ console.log(chalk.gray(' Hiç skill yüklü değil.\n'));
78
+ console.log(chalk.gray(' Yüklemek için: ') + chalk.cyan('natureco skills install <slug>'));
79
+ console.log(chalk.gray(' Gözatmak için: ') + chalk.cyan('natureco skills browse\n'));
78
80
  return;
79
81
  }
80
82
 
81
83
  allSkills.forEach((skill, index) => {
82
- const sourceLabel = skill.source === 'builtin' ? chalk.blue('[Yerleşik]') :
83
- skill.source === 'user' ? chalk.cyan('[Kişisel]') :
84
- chalk.magenta('[Proje]');
85
-
86
- console.log(chalk.white(`${index + 1}. ${skill.name}`) + ' ' + sourceLabel);
87
- console.log(chalk.gray(` ${skill.description}`));
88
-
84
+ const sourceLabel = skill.source === 'builtin' ? chalk.blue('[yerleşik]') :
85
+ skill.source === 'user' ? chalk.cyan('[kişisel]') :
86
+ chalk.magenta('[proje]');
87
+ console.log(chalk.white(` ${index + 1}. ${skill.name} `) + sourceLabel);
88
+ console.log(chalk.gray(` ${skill.description}`));
89
89
  if (skill.metadata?.requires?.bins) {
90
- console.log(chalk.gray(` Gerekli: ${skill.metadata.requires.bins.join(', ')}`));
90
+ console.log(chalk.gray(` Gerekli: ${skill.metadata.requires.bins.join(', ')}`));
91
91
  }
92
-
93
- if (skill.metadata?.os) {
94
- console.log(chalk.gray(` Platform: ${skill.metadata.os.join(', ')}`));
95
- }
96
-
97
92
  console.log('');
98
93
  });
99
94
 
100
- console.log(chalk.green('╰────────────────────╯\n'));
101
- console.log(chalk.gray(`Toplam: ${allSkills.length} skill\n`));
95
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
96
+ console.log(chalk.gray(` Toplam: ${allSkills.length} skill`));
97
+ console.log(chalk.gray(' Kaldırmak için: ') + chalk.cyan('natureco skills remove <slug>\n'));
102
98
  }
103
99
 
104
100
  async function installSkillCommand(slug) {
@@ -0,0 +1,84 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { getConfig } = require('../utils/config');
6
+ const { getSkills } = require('../utils/skills');
7
+
8
+ async function status() {
9
+ const config = getConfig();
10
+ const version = require('../../package.json').version;
11
+
12
+ console.log('');
13
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
14
+ console.log(chalk.cyan.bold('\n NatureCo CLI — Durum\n'));
15
+
16
+ // Provider
17
+ const providerHost = config.providerUrl
18
+ ? config.providerUrl.replace('https://', '').split('/')[0]
19
+ : null;
20
+
21
+ console.log(chalk.gray(' Versiyon : ') + chalk.white(`v${version}`));
22
+ if (providerHost) {
23
+ console.log(chalk.gray(' Provider : ') + chalk.white(providerHost));
24
+ console.log(chalk.gray(' Model : ') + chalk.white(config.providerModel || 'bilinmiyor'));
25
+ } else {
26
+ console.log(chalk.gray(' Provider : ') + chalk.yellow('yapılandırılmamış'));
27
+ }
28
+ if (config.botName) console.log(chalk.gray(' Bot : ') + chalk.cyan(config.botName));
29
+ if (config.userName) console.log(chalk.gray(' Kullanıcı : ') + chalk.white(config.userName));
30
+
31
+ // Gateway durumu
32
+ const pidFile = path.join(os.homedir(), '.natureco', 'gateway.pid');
33
+ const gatewayRunning = fs.existsSync(pidFile);
34
+ console.log(chalk.gray(' Gateway : ') + (gatewayRunning ? chalk.green('çalışıyor') : chalk.gray('durdurulmuş')));
35
+
36
+ // Dashboard
37
+ const dashPid = path.join(os.homedir(), '.natureco', 'dashboard.pid');
38
+ const dashRunning = fs.existsSync(dashPid);
39
+ console.log(chalk.gray(' Dashboard : ') + (dashRunning ? chalk.green('çalışıyor (localhost:3848)') : chalk.gray('durdurulmuş')));
40
+
41
+ // Kanallar
42
+ const channels = [];
43
+ if (config.telegramToken) channels.push('Telegram');
44
+ if (config.whatsappConnected) channels.push('WhatsApp');
45
+ if (config.discordToken) channels.push('Discord');
46
+ if (config.slackToken) channels.push('Slack');
47
+ console.log(chalk.gray(' Kanallar : ') + (channels.length > 0 ? chalk.white(channels.join(', ')) : chalk.gray('yok')));
48
+
49
+ // Skills
50
+ const skills = getSkills();
51
+ console.log(chalk.gray(' Skills : ') + chalk.white(`${skills.length} yüklü`));
52
+
53
+ // Memory
54
+ const memDir = path.join(os.homedir(), '.natureco', 'memory');
55
+ const memFiles = fs.existsSync(memDir) ? fs.readdirSync(memDir).filter(f => f.endsWith('.json')).length : 0;
56
+ console.log(chalk.gray(' Hafıza : ') + chalk.white(`${memFiles} bot`));
57
+
58
+ // Son session
59
+ const sessDir = path.join(os.homedir(), '.natureco', 'sessions');
60
+ if (fs.existsSync(sessDir)) {
61
+ const sessions = fs.readdirSync(sessDir).filter(f => f.endsWith('.json'));
62
+ if (sessions.length > 0) {
63
+ console.log(chalk.gray(' Sessions : ') + chalk.white(`${sessions.length} kayıtlı`));
64
+ }
65
+ }
66
+
67
+ // Son loglar
68
+ const logFile = path.join(os.homedir(), '.natureco', 'gateway.log');
69
+ if (fs.existsSync(logFile)) {
70
+ const lines = fs.readFileSync(logFile, 'utf-8').split('\n').filter(l => l.trim());
71
+ const last = lines.slice(-3);
72
+ if (last.length > 0) {
73
+ console.log(chalk.gray('\n Son Loglar:\n'));
74
+ last.forEach(l => console.log(chalk.gray(' ' + l)));
75
+ }
76
+ }
77
+
78
+ console.log('');
79
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
80
+ console.log(chalk.gray(' Detaylı kontrol: ') + chalk.cyan('natureco doctor'));
81
+ console.log(chalk.gray(' Kanal durumu : ') + chalk.cyan('natureco channels status\n'));
82
+ }
83
+
84
+ module.exports = status;
@@ -16,13 +16,24 @@ async function ultrareview(filePath) {
16
16
  }
17
17
 
18
18
  const config = getConfig();
19
- const defaultBotId = config.defaultBotId;
19
+ const defaultBotId = config.defaultBotId || config.botName || 'universal-provider';
20
20
 
21
- if (!defaultBotId) {
22
- console.log(chalk.red('\n❌ No default bot configured.\n'));
21
+ // getBots ile gerçek bot ID'yi al
22
+ const apiKey = getApiKey();
23
+ if (!apiKey && !config.providerApiKey) {
24
+ console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
23
25
  process.exit(1);
24
26
  }
25
27
 
28
+ let botId = defaultBotId;
29
+ try {
30
+ const botList = await getBots(apiKey || config.providerApiKey);
31
+ if (botList?.bots?.length > 0) {
32
+ const matched = botList.bots.find(b => b.name === config.botName) || botList.bots[0];
33
+ botId = matched.id;
34
+ }
35
+ } catch { /* fallback to defaultBotId */ }
36
+
26
37
  let code;
27
38
  let filename;
28
39
 
@@ -99,7 +110,7 @@ ${code}
99
110
  \`\`\``;
100
111
 
101
112
  try {
102
- const response = await sendMessage(apiKey, defaultBotId, prompt, null, '');
113
+ const response = await sendMessage(apiKey || config.providerApiKey, botId, prompt, null, '');
103
114
  const review = response.reply || response.message || 'No response';
104
115
 
105
116
  console.log(chalk.green('\n📊 Ultra Review:\n'));