natureco-cli 2.16.5 → 2.17.2

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.
@@ -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,107 @@
1
+ const chalk = require('chalk');
2
+ const { getConfig, saveConfig } = require('../utils/config');
3
+
4
+ async function pairing(args) {
5
+ const [action, ...params] = (args || []);
6
+
7
+ if (!action || action === 'list') return listPairing();
8
+ if (action === 'approve') return approvePairing(params[0], params[1]);
9
+ if (action === 'reject') return rejectPairing(params[0]);
10
+
11
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
12
+ console.log(chalk.gray(' Kullanım: natureco pairing [list|approve|reject]\n'));
13
+ process.exit(1);
14
+ }
15
+
16
+ function listPairing() {
17
+ const config = getConfig();
18
+ const pending = config.pendingPairings || [];
19
+
20
+ console.log('');
21
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
22
+ console.log(chalk.cyan.bold('\n Bekleyen Eşleştirmeler\n'));
23
+
24
+ if (pending.length === 0) {
25
+ console.log(chalk.gray(' Bekleyen eşleştirme yok.\n'));
26
+ console.log(chalk.gray(' Telegram bağlamak için: ') + chalk.cyan('natureco telegram connect'));
27
+ console.log(chalk.gray(' WhatsApp bağlamak için: ') + chalk.cyan('natureco whatsapp connect\n'));
28
+ return;
29
+ }
30
+
31
+ pending.forEach((p, i) => {
32
+ console.log(chalk.white(` ${i + 1}. ${p.channel} — ${p.id}`));
33
+ console.log(chalk.gray(` Kod: ${p.code} · ${new Date(p.createdAt).toLocaleString('tr-TR')}`));
34
+ console.log('');
35
+ });
36
+
37
+ console.log(chalk.gray(' Onaylamak için: ') + chalk.cyan('natureco pairing approve <kanal> <kod>'));
38
+ console.log(chalk.gray(' Reddetmek için: ') + chalk.cyan('natureco pairing reject <kod>\n'));
39
+ }
40
+
41
+ function approvePairing(channel, code) {
42
+ if (!channel || !code) {
43
+ console.log(chalk.red('\n ❌ Kanal ve kod gerekli\n'));
44
+ console.log(chalk.gray(' Kullanım: natureco pairing approve <telegram|whatsapp> <KOD>\n'));
45
+ process.exit(1);
46
+ }
47
+
48
+ const config = getConfig();
49
+ const pending = config.pendingPairings || [];
50
+ const idx = pending.findIndex(p => p.code === code && p.channel === channel);
51
+
52
+ if (idx === -1) {
53
+ console.log(chalk.yellow(`\n ⚠ Eşleştirme bulunamadı: ${channel} ${code}\n`));
54
+ console.log(chalk.gray(' Bekleyen listesi: ') + chalk.cyan('natureco pairing list\n'));
55
+ return;
56
+ }
57
+
58
+ const pairing = pending[idx];
59
+ pending.splice(idx, 1);
60
+
61
+ // Onaylanan kanalı izin listesine ekle
62
+ if (channel === 'telegram') {
63
+ const allowed = config.telegramAllowedChats || [];
64
+ if (!allowed.includes(pairing.id)) {
65
+ allowed.push(pairing.id);
66
+ config.telegramAllowedChats = allowed;
67
+ }
68
+ } else if (channel === 'whatsapp') {
69
+ const allowed = config.whatsappAllowedNumbers || [];
70
+ const num = pairing.id.replace(/\D/g, '');
71
+ if (!allowed.includes(num)) {
72
+ allowed.push(num);
73
+ config.whatsappAllowedNumbers = allowed;
74
+ }
75
+ }
76
+
77
+ config.pendingPairings = pending;
78
+ saveConfig(config);
79
+
80
+ console.log(chalk.green(`\n ✓ Eşleştirme onaylandı: ${channel} — ${pairing.id}\n`));
81
+ console.log(chalk.gray(' Gateway\'i yeniden başlatın: ') + chalk.cyan('natureco gateway stop && natureco gateway start\n'));
82
+ }
83
+
84
+ function rejectPairing(code) {
85
+ if (!code) {
86
+ console.log(chalk.red('\n ❌ Kod gerekli\n'));
87
+ console.log(chalk.gray(' Kullanım: natureco pairing reject <KOD>\n'));
88
+ process.exit(1);
89
+ }
90
+
91
+ const config = getConfig();
92
+ const pending = config.pendingPairings || [];
93
+ const idx = pending.findIndex(p => p.code === code);
94
+
95
+ if (idx === -1) {
96
+ console.log(chalk.yellow(`\n ⚠ Eşleştirme bulunamadı: ${code}\n`));
97
+ return;
98
+ }
99
+
100
+ pending.splice(idx, 1);
101
+ config.pendingPairings = pending;
102
+ saveConfig(config);
103
+
104
+ console.log(chalk.gray(`\n ○ Eşleştirme reddedildi: ${code}\n`));
105
+ }
106
+
107
+ module.exports = pairing;
@@ -0,0 +1,172 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+
6
+ const PLUGINS_DIR = path.join(os.homedir(), '.natureco', 'plugins');
7
+
8
+ function ensurePluginsDir() {
9
+ if (!fs.existsSync(PLUGINS_DIR)) fs.mkdirSync(PLUGINS_DIR, { recursive: true });
10
+ }
11
+
12
+ function loadPlugins() {
13
+ ensurePluginsDir();
14
+ if (!fs.existsSync(PLUGINS_DIR)) return [];
15
+ return fs.readdirSync(PLUGINS_DIR, { withFileTypes: true })
16
+ .filter(d => d.isDirectory())
17
+ .map(d => {
18
+ const metaFile = path.join(PLUGINS_DIR, d.name, 'plugin.json');
19
+ let meta = { name: d.name, description: '', version: '1.0.0', enabled: true };
20
+ if (fs.existsSync(metaFile)) {
21
+ try { meta = { ...meta, ...JSON.parse(fs.readFileSync(metaFile, 'utf-8')) }; } catch {}
22
+ }
23
+ // enabled durumu
24
+ const disabledFile = path.join(PLUGINS_DIR, d.name, '.disabled');
25
+ meta.enabled = !fs.existsSync(disabledFile);
26
+ return { ...meta, slug: d.name, path: path.join(PLUGINS_DIR, d.name) };
27
+ });
28
+ }
29
+
30
+ async function plugins(args) {
31
+ const [action, ...params] = (args || []);
32
+
33
+ if (!action || action === 'list') return listPlugins();
34
+ if (action === 'install') return installPlugin(params[0]);
35
+ if (action === 'enable') return togglePlugin(params[0], true);
36
+ if (action === 'disable') return togglePlugin(params[0], false);
37
+ if (action === 'info') return pluginInfo(params[0]);
38
+ if (action === 'doctor') return pluginDoctor();
39
+
40
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
41
+ console.log(chalk.gray(' Kullanım: natureco plugins [list|install|enable|disable|info|doctor]\n'));
42
+ process.exit(1);
43
+ }
44
+
45
+ function listPlugins() {
46
+ const list = loadPlugins();
47
+
48
+ console.log('');
49
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
50
+ console.log(chalk.cyan.bold('\n Plugins\n'));
51
+
52
+ if (list.length === 0) {
53
+ console.log(chalk.gray(' Yüklü plugin yok.\n'));
54
+ console.log(chalk.gray(' Yüklemek için: ') + chalk.cyan('natureco plugins install <npm-paketi>\n'));
55
+ return;
56
+ }
57
+
58
+ list.forEach(p => {
59
+ const status = p.enabled ? chalk.green('✓ aktif') : chalk.gray('○ pasif');
60
+ console.log(chalk.white(` ${p.name}`) + chalk.gray(` v${p.version} `) + status);
61
+ if (p.description) console.log(chalk.gray(` ${p.description}`));
62
+ console.log('');
63
+ });
64
+
65
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
66
+ console.log(chalk.gray(` Toplam: ${list.length} plugin`));
67
+ console.log(chalk.gray(' Devre dışı: ') + chalk.cyan('natureco plugins disable <slug>\n'));
68
+ }
69
+
70
+ async function installPlugin(pkg) {
71
+ if (!pkg) {
72
+ console.log(chalk.red('\n ❌ Paket adı gerekli\n'));
73
+ console.log(chalk.gray(' Kullanım: natureco plugins install <npm-paketi|./yerel-yol>\n'));
74
+ process.exit(1);
75
+ }
76
+
77
+ ensurePluginsDir();
78
+ console.log(chalk.gray(`\n "${pkg}" yükleniyor...\n`));
79
+
80
+ // Yerel path
81
+ if (pkg.startsWith('./') || pkg.startsWith('/')) {
82
+ const src = path.resolve(pkg);
83
+ if (!fs.existsSync(src)) {
84
+ console.log(chalk.red(`\n ❌ Yol bulunamadı: ${src}\n`));
85
+ process.exit(1);
86
+ }
87
+ const slug = path.basename(src);
88
+ const dest = path.join(PLUGINS_DIR, slug);
89
+ fs.cpSync(src, dest, { recursive: true });
90
+ console.log(chalk.green(`\n ✓ Plugin yüklendi: ${slug}\n`));
91
+ return;
92
+ }
93
+
94
+ // npm paketi — basit indirme
95
+ try {
96
+ const { execSync } = require('child_process');
97
+ const tmpDir = path.join(os.tmpdir(), `nc-plugin-${Date.now()}`);
98
+ fs.mkdirSync(tmpDir, { recursive: true });
99
+ execSync(`npm install ${pkg} --prefix ${tmpDir} --no-save`, { stdio: 'pipe' });
100
+ const pkgDir = path.join(tmpDir, 'node_modules', pkg.split('/').pop());
101
+ if (fs.existsSync(pkgDir)) {
102
+ const slug = pkg.replace(/[@\/]/g, '-').replace(/^-/, '');
103
+ const dest = path.join(PLUGINS_DIR, slug);
104
+ fs.cpSync(pkgDir, dest, { recursive: true });
105
+ fs.rmSync(tmpDir, { recursive: true, force: true });
106
+ console.log(chalk.green(`\n ✓ Plugin yüklendi: ${slug}\n`));
107
+ } else {
108
+ console.log(chalk.yellow('\n ⚠ Paket yüklendi ama plugin dizini bulunamadı.\n'));
109
+ }
110
+ } catch (e) {
111
+ console.log(chalk.red(`\n ❌ Yükleme başarısız: ${e.message}\n`));
112
+ process.exit(1);
113
+ }
114
+ }
115
+
116
+ function togglePlugin(slug, enable) {
117
+ if (!slug) {
118
+ console.log(chalk.red(`\n ❌ Plugin adı gerekli\n`));
119
+ process.exit(1);
120
+ }
121
+ const pluginDir = path.join(PLUGINS_DIR, slug);
122
+ if (!fs.existsSync(pluginDir)) {
123
+ console.log(chalk.red(`\n ❌ Plugin bulunamadı: ${slug}\n`));
124
+ process.exit(1);
125
+ }
126
+ const disabledFile = path.join(pluginDir, '.disabled');
127
+ if (enable) {
128
+ if (fs.existsSync(disabledFile)) fs.unlinkSync(disabledFile);
129
+ console.log(chalk.green(`\n ✓ Plugin aktif: ${slug}\n`));
130
+ } else {
131
+ fs.writeFileSync(disabledFile, '');
132
+ console.log(chalk.gray(`\n ○ Plugin pasif: ${slug}\n`));
133
+ }
134
+ }
135
+
136
+ function pluginInfo(slug) {
137
+ if (!slug) {
138
+ console.log(chalk.red('\n ❌ Plugin adı gerekli\n'));
139
+ process.exit(1);
140
+ }
141
+ const list = loadPlugins();
142
+ const p = list.find(x => x.slug === slug);
143
+ if (!p) {
144
+ console.log(chalk.red(`\n ❌ Plugin bulunamadı: ${slug}\n`));
145
+ process.exit(1);
146
+ }
147
+ console.log('');
148
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
149
+ console.log(chalk.cyan.bold(`\n Plugin: ${p.name}\n`));
150
+ console.log(chalk.gray(' Versiyon: ') + chalk.white(p.version));
151
+ console.log(chalk.gray(' Durum : ') + (p.enabled ? chalk.green('aktif') : chalk.gray('pasif')));
152
+ if (p.description) console.log(chalk.gray(' Açıklama: ') + chalk.white(p.description));
153
+ console.log(chalk.gray(' Yol : ') + chalk.gray(p.path));
154
+ console.log('');
155
+ }
156
+
157
+ function pluginDoctor() {
158
+ const list = loadPlugins();
159
+ console.log(chalk.cyan.bold('\n Plugin Tanılama\n'));
160
+ if (list.length === 0) {
161
+ console.log(chalk.gray(' Yüklü plugin yok.\n'));
162
+ return;
163
+ }
164
+ list.forEach(p => {
165
+ const hasIndex = fs.existsSync(path.join(p.path, 'index.js'));
166
+ const icon = hasIndex ? chalk.green('✓') : chalk.yellow('⚠');
167
+ console.log(` ${icon} ${p.name} ${hasIndex ? '' : chalk.yellow('(index.js bulunamadı)')}`);
168
+ });
169
+ console.log('');
170
+ }
171
+
172
+ module.exports = plugins;