natureco-cli 2.23.30 → 2.23.32

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.
Files changed (69) hide show
  1. package/bin/natureco.js +178 -167
  2. package/package.json +1 -1
  3. package/src/commands/acp.js +39 -0
  4. package/src/commands/admin-rpc.js +83 -0
  5. package/src/commands/agent.js +214 -23
  6. package/src/commands/agents.js +114 -30
  7. package/src/commands/approvals.js +172 -11
  8. package/src/commands/ask.js +1 -1
  9. package/src/commands/browser.js +815 -0
  10. package/src/commands/capability.js +195 -22
  11. package/src/commands/channels.js +422 -267
  12. package/src/commands/chat.js +5 -8
  13. package/src/commands/clawbot.js +19 -0
  14. package/src/commands/code.js +3 -2
  15. package/src/commands/commitments.js +125 -9
  16. package/src/commands/completion.js +40 -32
  17. package/src/commands/config.js +228 -30
  18. package/src/commands/configure.js +84 -67
  19. package/src/commands/cron.js +239 -19
  20. package/src/commands/daemon.js +34 -4
  21. package/src/commands/dashboard.js +47 -374
  22. package/src/commands/devices.js +53 -26
  23. package/src/commands/directory.js +146 -14
  24. package/src/commands/dns.js +148 -10
  25. package/src/commands/docs.js +119 -26
  26. package/src/commands/doctor.js +143 -492
  27. package/src/commands/exec-policy.js +57 -48
  28. package/src/commands/gateway.js +492 -249
  29. package/src/commands/health.js +141 -11
  30. package/src/commands/help.js +24 -25
  31. package/src/commands/hooks.js +141 -87
  32. package/src/commands/infer.js +1442 -41
  33. package/src/commands/logs.js +122 -99
  34. package/src/commands/mcp.js +121 -309
  35. package/src/commands/memory.js +128 -0
  36. package/src/commands/message.js +720 -140
  37. package/src/commands/models.js +39 -1
  38. package/src/commands/node.js +77 -77
  39. package/src/commands/nodes.js +278 -22
  40. package/src/commands/onboard.js +115 -56
  41. package/src/commands/pairing.js +108 -107
  42. package/src/commands/path.js +206 -0
  43. package/src/commands/plugins.js +35 -1
  44. package/src/commands/proxy.js +159 -8
  45. package/src/commands/qr.js +55 -13
  46. package/src/commands/reset.js +101 -94
  47. package/src/commands/secrets.js +104 -21
  48. package/src/commands/sessions.js +110 -51
  49. package/src/commands/setup.js +229 -649
  50. package/src/commands/skills.js +67 -1
  51. package/src/commands/status.js +101 -127
  52. package/src/commands/tasks.js +208 -100
  53. package/src/commands/terminal.js +130 -12
  54. package/src/commands/transcripts.js +24 -1
  55. package/src/commands/tui.js +41 -0
  56. package/src/commands/uninstall.js +73 -92
  57. package/src/commands/update.js +146 -91
  58. package/src/commands/web-fetch.js +34 -0
  59. package/src/commands/webhooks.js +58 -66
  60. package/src/commands/wiki.js +783 -0
  61. package/src/utils/agents-md.js +85 -0
  62. package/src/utils/api.js +40 -41
  63. package/src/utils/format.js +144 -0
  64. package/src/utils/headless.js +2 -1
  65. package/src/utils/parallel-tools.js +106 -0
  66. package/src/utils/sub-agent.js +148 -0
  67. package/src/utils/token-budget.js +304 -0
  68. package/src/utils/tool-runner.js +7 -5
  69. package/src/utils/web-fetch.js +107 -0
@@ -1,492 +1,143 @@
1
- const chalk = require('chalk');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const net = require('net');
5
- const { getConfig, saveConfig, CONFIG_FILE, CONFIG_DIR, listBackups } = require('../utils/config');
6
- const { getBots } = require('../utils/api');
7
- const { getSkills } = require('../utils/skills');
8
- const { NatureCoError, ApiError, ConfigError, handleError } = require('../utils/errors');
9
- const { listSecretRefs, resolveSecretRef } = require('../utils/secrets');
10
- const { loadApprovals, resolveEffectivePolicy } = require('../utils/approvals');
11
-
12
- async function doctor(options) {
13
- const shouldFix = options && (options.includes('--fix') || options.includes('-f'));
14
-
15
- console.clear();
16
- console.log('');
17
- console.log(chalk.green.bold(' (\\_/)'));
18
- console.log(chalk.green.bold(' (•ᴥ•)'));
19
- console.log(chalk.green(' />🌿'));
20
- console.log('');
21
- console.log(chalk.green.bold(' NatureCo CLI — Sistem Kontrolü'));
22
- if (shouldFix) console.log(chalk.yellow(' --fix modu aktif\n'));
23
- else console.log(chalk.gray(' Sorunları otomatik düzeltmek için: natureco doctor --fix\n'));
24
- console.log(chalk.gray(' ' + '─'.repeat(48)));
25
- console.log('');
26
-
27
- const results = [];
28
- let passed = 0;
29
- let total = 0;
30
- let criticalPassed = 0;
31
- let criticalTotal = 0;
32
-
33
- // 1. Node.js versiyon kontrolü
34
- total++;
35
- criticalTotal++;
36
- const nodeVersion = process.version;
37
- const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0]);
38
- if (nodeMajor >= 18) {
39
- console.log(chalk.green(' Node.js versiyonu:'), chalk.white(nodeVersion));
40
- passed++;
41
- criticalPassed++;
42
- results.push({ status: 'ok', message: 'Node.js 18+' });
43
- } else {
44
- console.log(chalk.red('❌ Node.js versiyonu:'), chalk.white(nodeVersion), chalk.gray('(18+ gerekli)'));
45
- results.push({ status: 'error', message: 'Node.js 18+ gerekli', fix: 'Node.js güncelleyin: https://nodejs.org' });
46
- }
47
-
48
- // 2. Config dosyası kontrolü
49
- total++;
50
- criticalTotal++;
51
- let config = null;
52
- if (fs.existsSync(CONFIG_FILE)) {
53
- try {
54
- const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
55
- config = JSON.parse(data);
56
- console.log(chalk.green('✅ Config dosyası:'), chalk.white(CONFIG_FILE));
57
- passed++;
58
- criticalPassed++;
59
- results.push({ status: 'ok', message: 'Config dosyası geçerli' });
60
- } catch (err) {
61
- console.log(chalk.red(' Config dosyası:'), chalk.white('Bozuk JSON'));
62
- results.push({ status: 'error', message: 'Config dosyası bozuk', fix: 'Config yeniden oluşturulacak' });
63
-
64
- if (shouldFix) {
65
- console.log(chalk.yellow(' 🔧 Config yeniden oluşturuluyor...'));
66
- config = { setupCompleted: false, skills: { enabled: true, list: [] }, mcpServers: {} };
67
- saveConfig(config);
68
- console.log(chalk.green(' ✓ Config oluşturuldu'));
69
- }
70
- }
71
- } else {
72
- console.log(chalk.yellow('⚠️ Config dosyası:'), chalk.white('Bulunamadı'));
73
- results.push({ status: 'warning', message: 'Config dosyası yok', fix: 'Config oluşturulacak' });
74
-
75
- if (shouldFix) {
76
- console.log(chalk.yellow(' 🔧 Config oluşturuluyor...'));
77
- if (!fs.existsSync(CONFIG_DIR)) {
78
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
79
- }
80
- config = { setupCompleted: false, skills: { enabled: true, list: [] }, mcpServers: {} };
81
- saveConfig(config);
82
- console.log(chalk.green(' ✓ Config oluşturuldu'));
83
- }
84
- }
85
-
86
- // 3. Provider URL kontrolü (v2.x)
87
- total++;
88
- criticalTotal++;
89
- let providerValid = false;
90
- if (config && config.providerUrl) {
91
- console.log(chalk.green('✅ Provider URL:'), chalk.white(config.providerUrl));
92
- passed++;
93
- criticalPassed++;
94
- providerValid = true;
95
- results.push({ status: 'ok', message: 'Provider URL ayarlanmış' });
96
- } else {
97
- console.log(chalk.red('❌ Provider URL:'), chalk.white('Ayarlanmamış'));
98
- results.push({ status: 'error', message: 'Provider URL yok', fix: 'Setup çalıştırılacak' });
99
- }
100
-
101
- if (shouldFix && !providerValid) {
102
- console.log(chalk.yellow(' 🔧 Setup başlatılıyor...'));
103
- console.log(chalk.gray(' Lütfen setup\'ı manuel çalıştırın: natureco setup'));
104
- }
105
-
106
- // 4. Provider Model kontrolü (v2.x)
107
- total++;
108
- if (config && config.providerModel) {
109
- console.log(chalk.green(' Provider Model:'), chalk.white(config.providerModel));
110
- passed++;
111
- results.push({ status: 'ok', message: 'Provider model ayarlanmış' });
112
- } else {
113
- console.log(chalk.yellow('⚠️ Provider Model:'), chalk.white('Ayarlanmamış'));
114
- results.push({ status: 'warning', message: 'Provider model yok', fix: 'Setup ile ayarlanabilir' });
115
- }
116
-
117
- // 5. Skills kontrolü
118
- total++;
119
- try {
120
- const skills = getSkills();
121
- const skillCount = skills.length;
122
-
123
- if (skillCount > 0) {
124
- console.log(chalk.green('✅ Skills:'), chalk.white(`${skillCount} yüklü`));
125
- passed++;
126
- results.push({ status: 'ok', message: `${skillCount} skill yüklü` });
127
- } else {
128
- console.log(chalk.yellow('⚠️ Skills:'), chalk.white('Yüklü skill yok'));
129
- results.push({ status: 'warning', message: 'Skill yok', fix: 'Yerleşik skill\'ler yüklenebilir' });
130
- }
131
- } catch (err) {
132
- console.log(chalk.yellow('⚠️ Skills:'), chalk.white('Kontrol edilemedi'));
133
- results.push({ status: 'warning', message: 'Skills kontrol edilemedi' });
134
- }
135
-
136
- // 6. MCP sunucuları kontrolü
137
- total++;
138
- const mcpCount = config && config.mcpServers ? Object.keys(config.mcpServers).length : 0;
139
- if (mcpCount > 0) {
140
- console.log(chalk.green('✅ MCP sunucuları:'), chalk.white(`${mcpCount} kayıtlı`));
141
- passed++;
142
- results.push({ status: 'ok', message: `${mcpCount} MCP sunucusu` });
143
- } else {
144
- console.log(chalk.yellow('⚠️ MCP sunucuları:'), chalk.white('Kayıtlı sunucu yok'));
145
- results.push({ status: 'warning', message: 'MCP sunucusu yok' });
146
- }
147
-
148
- // 7. Hafıza kontrolü
149
- total++;
150
- const memoryDir = path.join(CONFIG_DIR, 'memory');
151
- let memoryActive = false;
152
-
153
- if (fs.existsSync(memoryDir)) {
154
- try {
155
- const files = fs.readdirSync(memoryDir);
156
- const memoryFiles = files.filter(f => f.endsWith('.json'));
157
- if (memoryFiles.length > 0) {
158
- memoryActive = true;
159
- console.log(chalk.green('✅ Hafıza:'), chalk.white(`Aktif (${memoryFiles.length} dosya)`));
160
- passed++;
161
- results.push({ status: 'ok', message: `Hafıza aktif (${memoryFiles.length} dosya)` });
162
- } else {
163
- console.log(chalk.yellow('⚠️ Hafıza:'), chalk.white('Klasör var, dosya yok'));
164
- results.push({ status: 'warning', message: 'Hafıza dosyası yok' });
165
- }
166
- } catch (err) {
167
- console.log(chalk.yellow('⚠️ Hafıza:'), chalk.white('Kontrol edilemedi'));
168
- results.push({ status: 'warning', message: 'Hafıza kontrol edilemedi' });
169
- }
170
- } else {
171
- console.log(chalk.yellow('⚠️ Hafıza:'), chalk.white('Pasif'));
172
- results.push({ status: 'warning', message: 'Hafıza pasif' });
173
- }
174
-
175
- // 8. Telegram kontrolü (opsiyonel)
176
- if (config && config.telegramToken) {
177
- console.log(chalk.green('✅ Telegram:'), chalk.white('Bağlı'));
178
- results.push({ status: 'ok', message: 'Telegram bağlı', optional: true });
179
- } else {
180
- console.log(chalk.gray('⚪ Telegram:'), chalk.white('Bağlı değil'));
181
- results.push({ status: 'info', message: 'Telegram bağlı değil', optional: true });
182
- }
183
-
184
- // 9. Discord kontrolü (opsiyonel)
185
- if (config && config.discordToken) {
186
- console.log(chalk.green('✅ Discord:'), chalk.white('Bağlı'));
187
- results.push({ status: 'ok', message: 'Discord bağlı', optional: true });
188
- } else {
189
- console.log(chalk.gray('⚪ Discord:'), chalk.white('Bağlı değil'));
190
- results.push({ status: 'info', message: 'Discord bağlı değil', optional: true });
191
- }
192
-
193
- // 10. Slack kontrolü (opsiyonel)
194
- if (config && config.slackToken) {
195
- console.log(chalk.green('✅ Slack:'), chalk.white('Bağlı'));
196
- results.push({ status: 'ok', message: 'Slack bağlı', optional: true });
197
- } else {
198
- console.log(chalk.gray('⚪ Slack:'), chalk.white('Bağlı değil'));
199
- results.push({ status: 'info', message: 'Slack bağlı değil', optional: true });
200
- }
201
-
202
- // 11. WhatsApp kontrolü (opsiyonel)
203
- if (config && config.whatsappConnected) {
204
- console.log(chalk.green('✅ WhatsApp:'), chalk.white('Bağlı'));
205
- results.push({ status: 'ok', message: 'WhatsApp bağlı', optional: true });
206
- } else {
207
- console.log(chalk.gray('⚪ WhatsApp:'), chalk.white('Bağlı değil'));
208
- results.push({ status: 'info', message: 'WhatsApp bağlı değil', optional: true });
209
- }
210
-
211
- // 12. Dashboard kontrolü (opsiyonel)
212
- const dashboardRunning = await checkPort(3848);
213
- if (dashboardRunning) {
214
- console.log(chalk.green('✅ Dashboard:'), chalk.white('Çalışıyor (port 3848)'));
215
- results.push({ status: 'ok', message: 'Dashboard çalışıyor', optional: true });
216
- } else {
217
- console.log(chalk.gray('⚪ Dashboard:'), chalk.white('Çalışmıyor'));
218
- results.push({ status: 'info', message: 'Dashboard çalışmıyor', fix: 'natureco dashboard start', optional: true });
219
-
220
- if (shouldFix) {
221
- console.log(chalk.yellow(' 🔧 Dashboard başlatılıyor...'));
222
- console.log(chalk.gray(' Lütfen manuel başlatın: natureco dashboard start'));
223
- }
224
- }
225
-
226
- // 13. Gateway kontrolü (opsiyonel)
227
- const gatewayPidFile = path.join(CONFIG_DIR, 'gateway.pid');
228
- let gatewayRunning = false;
229
-
230
- if (fs.existsSync(gatewayPidFile)) {
231
- try {
232
- const pid = parseInt(fs.readFileSync(gatewayPidFile, 'utf-8').trim());
233
-
234
- // Check if process is running
235
- try {
236
- process.kill(pid, 0); // Signal 0 checks if process exists
237
- gatewayRunning = true;
238
- console.log(chalk.green('✅ Gateway:'), chalk.white(`Çalışıyor (PID: ${pid})`));
239
- results.push({ status: 'ok', message: 'Gateway çalışıyor', optional: true });
240
- } catch {
241
- console.log(chalk.yellow('⚠️ Gateway:'), chalk.white('PID dosyası var ama process çalışmıyor'));
242
- results.push({ status: 'warning', message: 'Gateway PID stale', fix: 'natureco gateway start', optional: true });
243
- }
244
- } catch {
245
- console.log(chalk.gray('⚪ Gateway:'), chalk.white('PID dosyası okunamadı'));
246
- results.push({ status: 'info', message: 'Gateway PID okunamadı', optional: true });
247
- }
248
- } else {
249
- console.log(chalk.gray('⚪ Gateway:'), chalk.white('Çalışmıyor'));
250
- results.push({ status: 'info', message: 'Gateway çalışmıyor', fix: 'natureco gateway start', optional: true });
251
- }
252
-
253
- // 14. Tavily API key kontrolü (web search için)
254
- total++;
255
- if (config && config.tavilyApiKey) {
256
- console.log(chalk.green('✅ Tavily API:'), chalk.white(config.tavilyApiKey.slice(0, 10) + '...'));
257
- passed++;
258
- results.push({ status: 'ok', message: 'Tavily API key ayarlanmış' });
259
- } else {
260
- console.log(chalk.yellow('⚠️ Tavily API:'), chalk.white('Ayarlanmamış (web search çalışmayacak)'));
261
- results.push({ status: 'warning', message: 'Tavily API key yok', fix: 'natureco config set tavilyApiKey tvly_xxx' });
262
- }
263
-
264
- // 15. Provider erişim kontrolü (gerçek API doğrulama)
265
- total++;
266
- criticalTotal++;
267
- if (config && config.providerUrl) {
268
- try {
269
- const response = await fetch(config.providerUrl, {
270
- method: 'POST',
271
- headers: {
272
- 'Content-Type': 'application/json',
273
- ...(config.providerApiKey ? { 'Authorization': `Bearer ${config.providerApiKey}` } : {}),
274
- },
275
- body: JSON.stringify({
276
- model: config.providerModel || 'llama-3.3-70b-versatile',
277
- messages: [{ role: 'user', content: 'test' }],
278
- max_tokens: 1,
279
- }),
280
- signal: AbortSignal.timeout(15000),
281
- });
282
-
283
- if (response.ok) {
284
- console.log(chalk.green('✅ Provider:'), chalk.white('API doğrulandı'));
285
- passed++;
286
- criticalPassed++;
287
- results.push({ status: 'ok', message: 'Provider API doğrulandı' });
288
- } else if (response.status === 401 || response.status === 403) {
289
- console.log(chalk.red('❌ Provider:'), chalk.white('API key geçersiz'));
290
- results.push({ status: 'error', message: 'API key reddedildi', fix: 'natureco config set providerApiKey <key>' });
291
- } else {
292
- console.log(chalk.yellow('⚠️ Provider:'), chalk.white(`HTTP ${response.status} — beklenmeyen yanıt`));
293
- results.push({ status: 'warning', message: `Provider HTTP ${response.status} döndü` });
294
- }
295
- } catch (err) {
296
- console.log(chalk.red('❌ Provider:'), chalk.white('Bağlantı hatası: ' + err.message));
297
- results.push({ status: 'error', message: 'Provider bağlantı hatası' });
298
- }
299
- } else {
300
- console.log(chalk.gray('⚪ Provider Erişim:'), chalk.white('Provider URL ayarlanmamış'));
301
- results.push({ status: 'info', message: 'Provider URL yok' });
302
- }
303
-
304
- // 16. Güncelleme kontrolü
305
- total++;
306
- try {
307
- const packageJson = require('../../package.json');
308
- const currentVersion = packageJson.version;
309
-
310
- const response = await fetch('https://registry.npmjs.org/natureco-cli/latest', {
311
- signal: AbortSignal.timeout(5000),
312
- });
313
-
314
- if (response.ok) {
315
- const data = await response.json();
316
- const latestVersion = data.version;
317
-
318
- if (currentVersion === latestVersion) {
319
- console.log(chalk.green('✅ Versiyon:'), chalk.white(`${currentVersion} (güncel)`));
320
- passed++;
321
- results.push({ status: 'ok', message: 'Versiyon güncel' });
322
- } else {
323
- console.log(chalk.yellow('⚠️ Versiyon:'), chalk.white(`${currentVersion} → ${latestVersion} mevcut`));
324
- results.push({ status: 'warning', message: 'Yeni versiyon mevcut', fix: 'npm install -g natureco-cli@latest' });
325
-
326
- if (shouldFix) {
327
- console.log(chalk.yellow(' 🔧 Güncelleme başlatılıyor...'));
328
- console.log(chalk.gray(' Lütfen manuel güncelleyin: npm install -g natureco-cli@latest'));
329
- }
330
- }
331
- } else {
332
- console.log(chalk.gray('⚪ Versiyon:'), chalk.white('Kontrol edilemedi'));
333
- results.push({ status: 'info', message: 'Versiyon kontrol edilemedi' });
334
- }
335
- } catch (err) {
336
- console.log(chalk.red('Bağlantı hatası: ' + err.message));
337
- results.push({ status: 'info', message: 'Versiyon kontrol edilemedi' });
338
- }
339
-
340
- // 17. Exec approvals kontrolü
341
- total++;
342
- const approvals = loadApprovals();
343
- const policy = resolveEffectivePolicy(approvals);
344
- const allowlistCount = (approvals.allowlist || []).length;
345
- if (policy !== 'deny' || allowlistCount > 0) {
346
- passed++;
347
- results.push({ status: 'ok', message: `Güvenlik: ${policy} (${allowlistCount} allowlist)` });
348
- console.log(chalk.green('✅ Exec Güvenlik:'), chalk.white(`${policy} — ${allowlistCount} allowlist kuralı`));
349
- } else {
350
- results.push({ status: 'warning', message: 'Exec güvenlik deny modunda', fix: 'natureco security policy set allowlist' });
351
- console.log(chalk.yellow('⚠️ Exec Güvenlik:'), chalk.white('deny modunda, komut çalıştırma engelli'));
352
- }
353
-
354
- // 18. Exec approvals allowlist durumu (opsiyonel detay)
355
- if (allowlistCount > 0) {
356
- const expired = (approvals.allowlist || []).filter(e => e.expires && new Date(e.expires) < new Date()).length;
357
- if (expired > 0) {
358
- console.log(chalk.yellow(` ⚠ ${expired} allowlist kuralı süresi dolmuş`));
359
- }
360
- }
361
-
362
- // 19. Config yedekleri kontrolü
363
- total++;
364
- try {
365
- const backupDir = path.join(CONFIG_DIR, 'backups');
366
- if (fs.existsSync(backupDir)) {
367
- const backups = fs.readdirSync(backupDir).filter(f => f.startsWith('config-') && f.endsWith('.json'));
368
- if (backups.length > 0) {
369
- passed++;
370
- results.push({ status: 'ok', message: `${backups.length} config yedeği mevcut` });
371
- console.log(chalk.green('✅ Config Yedek:'), chalk.white(`${backups.length} yedek`));
372
-
373
- if (backups.length >= 10) {
374
- console.log(chalk.yellow(' ⚠ Maksimum yedek sayısına ulaşıldı (10)'));
375
- }
376
- } else {
377
- results.push({ status: 'info', message: 'Config yedeği yok' });
378
- console.log(chalk.gray('⚪ Config Yedek:'), chalk.white('Yedek bulunamadı'));
379
- }
380
- } else {
381
- results.push({ status: 'info', message: 'Config yedeği yok' });
382
- console.log(chalk.gray('⚪ Config Yedek:'), chalk.white('Yedek klasörü yok'));
383
- }
384
- } catch {
385
- results.push({ status: 'info', message: 'Config yedekleri kontrol edilemedi' });
386
- console.log(chalk.gray('⚪ Config Yedek:'), chalk.white('Kontrol edilemedi'));
387
- }
388
-
389
- // 20. Secret referansları kontrolü
390
- total++;
391
- try {
392
- const secretRefs = listSecretRefs(config || {});
393
- if (secretRefs.length > 0) {
394
- const unresolved = secretRefs.filter(ref => {
395
- try {
396
- return resolveSecretRef(ref) === null;
397
- } catch {
398
- return true;
399
- }
400
- });
401
-
402
- if (unresolved.length === 0) {
403
- passed++;
404
- results.push({ status: 'ok', message: `${secretRefs.length} secret referansı çözüldü` });
405
- console.log(chalk.green('✅ Secrets:'), chalk.white(`${secretRefs.length} referans çözüldü`));
406
- } else {
407
- results.push({ status: 'warning', message: `${unresolved.length}/${secretRefs.length} secret çözülemedi`, fix: '.env dosyası oluşturun' });
408
- console.log(chalk.yellow('⚠️ Secrets:'), chalk.white(`${unresolved.length}/${secretRefs.length} referans çözülemedi`));
409
- }
410
- } else {
411
- results.push({ status: 'info', message: 'Secret referansı yok' });
412
- console.log(chalk.gray('⚪ Secrets:'), chalk.white('Referans yok'));
413
- }
414
- } catch {
415
- results.push({ status: 'info', message: 'Secrets kontrol edilemedi' });
416
- console.log(chalk.gray('⚪ Secrets:'), chalk.white('Kontrol edilemedi'));
417
- }
418
-
419
- // 21. .env dosyası varlığı (secret ref varsa)
420
- if (config) {
421
- const envFile = path.join(CONFIG_DIR, '.env');
422
- if (fs.existsSync(envFile)) {
423
- console.log(chalk.gray(' 📄 .env dosyası:'), chalk.white('Mevcut'));
424
- } else {
425
- console.log(chalk.gray(' 📄 .env dosyası:'), chalk.white('Yok'));
426
- }
427
- }
428
-
429
- // Özet
430
- console.log('');
431
- console.log(chalk.gray(' ' + '─'.repeat(48)));
432
-
433
- const criticalPercentage = criticalTotal > 0 ? Math.round((criticalPassed / criticalTotal) * 100) : 100;
434
- let statusColor = chalk.green;
435
- let statusText = 'Mükemmel';
436
-
437
- if (criticalPercentage < 75) {
438
- statusColor = chalk.red;
439
- statusText = 'Kritik sorun var';
440
- } else if (criticalPercentage < 100) {
441
- statusColor = chalk.yellow;
442
- statusText = 'Dikkat gerekiyor';
443
- } else if (passed < total) {
444
- statusColor = chalk.cyan;
445
- statusText = 'İyi';
446
- }
447
-
448
- console.log('');
449
- console.log(chalk.gray(' Durum : ') + statusColor.bold(statusText));
450
- console.log(chalk.gray(' Kontrol : ') + chalk.white(`${passed}/${total} geçti`));
451
- console.log(chalk.gray(' Kritik : ') + chalk.white(`${criticalPassed}/${criticalTotal}`));
452
-
453
- const errors = results.filter(r => r.status === 'error');
454
- const warnings = results.filter(r => r.status === 'warning');
455
-
456
- if (errors.length > 0) {
457
- console.log(chalk.red(`\n ❌ ${errors.length} hata bulundu`));
458
- }
459
- if (warnings.length > 0) {
460
- console.log(chalk.yellow(` ⚠ ${warnings.length} uyarı bulundu`));
461
- }
462
- if (!shouldFix && (errors.length > 0 || warnings.length > 0)) {
463
- console.log(chalk.gray('\n Otomatik onarım: ') + chalk.cyan('natureco doctor --fix'));
464
- }
465
- console.log('');
466
- }
467
-
468
- function checkPort(port) {
469
- return new Promise((resolve) => {
470
- const socket = new net.Socket();
471
-
472
- socket.setTimeout(1000);
473
-
474
- socket.on('connect', () => {
475
- socket.destroy();
476
- resolve(true);
477
- });
478
-
479
- socket.on('timeout', () => {
480
- socket.destroy();
481
- resolve(false);
482
- });
483
-
484
- socket.on('error', () => {
485
- resolve(false);
486
- });
487
-
488
- socket.connect(port, '127.0.0.1');
489
- });
490
- }
491
-
492
- module.exports = doctor;
1
+ const chalk = require('chalk');
2
+ const F = require('../utils/format');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const BASE_DIR = path.join(os.homedir(), '.natureco');
8
+ const CONFIG_FILE = path.join(BASE_DIR, 'config.json');
9
+
10
+ const CHECKS = [
11
+ { name: 'configExists', label: 'Config file exists' },
12
+ { name: 'nodeVersion', label: 'Node.js version >= 18' },
13
+ { name: 'npmPackages', label: 'Required npm packages installed' },
14
+ { name: 'diskSpace', label: 'Sufficient disk space' },
15
+ { name: 'writePermission', label: 'Write permission on ~/.natureco' },
16
+ ];
17
+
18
+ function doctor(params) {
19
+ try {
20
+ const [action, checkName] = params || [];
21
+
22
+ if (!action || action === 'run') return cmdRun();
23
+ if (action === 'list') return cmdList();
24
+ if (action === 'check') return cmdCheck(checkName);
25
+
26
+ console.log(chalk.red(`\n Unknown doctor action: ${action}\n`));
27
+ console.log(chalk.gray(' Usage: natureco doctor [run|list|check <name>]\n'));
28
+ } catch (err) {
29
+ console.log(chalk.red(`\n Doctor error: ${err.message}\n`));
30
+ }
31
+ }
32
+
33
+ function cmdList() {
34
+ F.list(CHECKS.map(c => ({ label: c.name, value: c.label })));
35
+ }
36
+
37
+ function cmdCheck(name) {
38
+ if (!name) {
39
+ console.log(chalk.red('\n Usage: natureco doctor check <name>\n'));
40
+ console.log(chalk.gray(' Available checks: ' + CHECKS.map(c => c.name).join(', ') + '\n'));
41
+ return;
42
+ }
43
+
44
+ const check = CHECKS.find(c => c.name === name);
45
+ if (!check) {
46
+ console.log(chalk.red(`\n Unknown check: ${name}\n`));
47
+ console.log(chalk.gray(' Available checks: ' + CHECKS.map(c => c.name).join(', ') + '\n'));
48
+ return;
49
+ }
50
+
51
+ const result = runCheck(name);
52
+ F.kv('Check', check.label);
53
+ if (result.pass) {
54
+ F.success(result.message);
55
+ } else {
56
+ F.error(result.message);
57
+ }
58
+ }
59
+
60
+ function cmdRun() {
61
+ F.header('System Doctor');
62
+
63
+ const rows = [];
64
+ let passed = 0;
65
+ let failed = 0;
66
+
67
+ for (const check of CHECKS) {
68
+ const result = runCheck(check.name);
69
+ rows.push([
70
+ check.label,
71
+ result.pass ? '✓' : '✗',
72
+ result.message,
73
+ ]);
74
+ if (result.pass) passed++; else failed++;
75
+ }
76
+
77
+ F.table(['Check', 'Status', 'Message'], rows);
78
+
79
+ const total = passed + failed;
80
+ F.meta(`${passed}/${total} checks passed`);
81
+
82
+ if (failed > 0) {
83
+ F.warning('Some checks failed. Run individual checks for details.');
84
+ }
85
+ }
86
+
87
+ function runCheck(name) {
88
+ switch (name) {
89
+ case 'configExists':
90
+ return {
91
+ pass: fs.existsSync(CONFIG_FILE),
92
+ message: fs.existsSync(CONFIG_FILE) ? 'Found at ' + CONFIG_FILE : 'Missing at ' + CONFIG_FILE,
93
+ };
94
+
95
+ case 'nodeVersion': {
96
+ const v = process.version.slice(1).split('.')[0];
97
+ const ok = parseInt(v) >= 18;
98
+ return {
99
+ pass: ok,
100
+ message: ok ? 'Running Node ' + process.version : 'Node 18+ required, found ' + process.version,
101
+ };
102
+ }
103
+
104
+ case 'npmPackages': {
105
+ const required = ['chalk', 'commander'];
106
+ const missing = required.filter(p => { try { require.resolve(p); return false; } catch { return true; } });
107
+ return {
108
+ pass: missing.length === 0,
109
+ message: missing.length === 0 ? 'All dependencies installed' : 'Missing: ' + missing.join(', '),
110
+ };
111
+ }
112
+
113
+ case 'diskSpace': {
114
+ try {
115
+ const stats = require('os').freemem();
116
+ const freeGB = stats / 1024 / 1024 / 1024;
117
+ return {
118
+ pass: freeGB > 0.5,
119
+ message: freeGB > 0.5 ? `${freeGB.toFixed(1)} GB free` : 'Less than 500 MB free',
120
+ };
121
+ } catch {
122
+ return { pass: true, message: 'Unable to check' };
123
+ }
124
+ }
125
+
126
+ case 'writePermission': {
127
+ try {
128
+ if (!fs.existsSync(BASE_DIR)) fs.mkdirSync(BASE_DIR, { recursive: true });
129
+ const testFile = path.join(BASE_DIR, '.write-test');
130
+ fs.writeFileSync(testFile, 'test');
131
+ fs.unlinkSync(testFile);
132
+ return { pass: true, message: 'Write access OK' };
133
+ } catch (e) {
134
+ return { pass: false, message: e.message };
135
+ }
136
+ }
137
+
138
+ default:
139
+ return { pass: false, message: 'Unknown check' };
140
+ }
141
+ }
142
+
143
+ module.exports = doctor;