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,249 +1,492 @@
1
- const chalk = require('chalk');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
5
- const packageJson = require('../../package.json');
6
- const { getConfig, CONFIG_FILE } = require('../utils/config');
7
- const { getSkills } = require('../utils/skills');
8
- const { getMcpServers } = require('../utils/mcp');
9
- const { loadBaileys } = require('../utils/baileys');
10
- const { NatureCoError, GatewayError, handleError } = require('../utils/errors');
11
- const pino = require('pino');
12
- const logger = pino({ level: 'silent' });
13
-
14
- async function gateway(action) {
15
- if (action === 'start') {
16
- return startGateway();
17
- }
18
-
19
- // Config kontrolü - yoksa veya apiKey yoksa setup çalıştır
20
- if (!fs.existsSync(CONFIG_FILE)) {
21
- const setup = require('./setup');
22
- await setup();
23
- return;
24
- }
25
-
26
- const config = getConfig();
27
-
28
- if (!config || !config.apiKey) {
29
- const setup = require('./setup');
30
- await setup();
31
- return;
32
- }
33
-
34
- // Normal gateway ekranı
35
- const version = packageJson.version;
36
-
37
- console.clear();
38
-
39
- console.log('');
40
- console.log(chalk.green.bold(' (\\_/)'));
41
- console.log(chalk.green.bold(' (•ᴥ•)'));
42
- console.log(chalk.green(' />🌿'));
43
- console.log('');
44
- console.log(chalk.green.bold(' NatureCo CLI ') + chalk.gray(`v${version}`));
45
- console.log(chalk.gray(' Terminal-native AI agent\n'));
46
- console.log(chalk.gray(' ' + '─'.repeat(48)));
47
- console.log('');
48
-
49
- // Durum bilgileri
50
- const providerHost = config.providerUrl
51
- ? config.providerUrl.replace('https://', '').split('/')[0]
52
- : null;
53
-
54
- if (providerHost) {
55
- console.log(chalk.gray(' Provider : ') + chalk.white(providerHost));
56
- }
57
- if (config.providerModel) {
58
- console.log(chalk.gray(' Model : ') + chalk.white(config.providerModel));
59
- }
60
- if (config.botName) {
61
- console.log(chalk.gray(' Bot : ') + chalk.cyan(config.botName));
62
- }
63
- if (config.userName) {
64
- console.log(chalk.gray(' Kullanıcı: ') + chalk.white(config.userName));
65
- }
66
-
67
- const skills = getSkills();
68
- console.log(chalk.gray(' Skills : ') + chalk.white(`${skills.length} yüklü`));
69
-
70
- const mcpServers = getMcpServers();
71
- const activeMcp = Object.values(mcpServers).filter(s => !s.disabled).length;
72
- const totalMcp = Object.keys(mcpServers).length;
73
- if (totalMcp > 0) {
74
- console.log(chalk.gray(' MCP : ') + chalk.white(`${activeMcp}/${totalMcp} aktif`));
75
- }
76
-
77
- console.log('');
78
- console.log(chalk.gray(' ' + '─'.repeat(48)));
79
- console.log('');
80
-
81
- // Hızlı komutlar
82
- console.log(chalk.cyan.bold(' Hızlı Başlat\n'));
83
- console.log(chalk.yellow(' natureco chat') + chalk.gray(' Bot ile sohbet başlat'));
84
- console.log(chalk.yellow(' natureco setup') + chalk.gray(' Kurulumu yeniden çalıştır'));
85
- console.log(chalk.yellow(' natureco gateway start') + chalk.gray(' Gateway\'i arka planda başlat'));
86
- console.log(chalk.yellow(' natureco help') + chalk.gray(' Tüm komutları göster'));
87
- console.log('');
88
- console.log(chalk.gray(' ' + ''.repeat(48)));
89
- console.log(chalk.gray(' Döküman: ') + chalk.cyan('natureco.me/cli'));
90
- console.log('');
91
- }
92
-
93
- async function startGateway() {
94
- const config = getConfig();
95
-
96
- if (!config || !config.apiKey) {
97
- console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
98
- process.exit(1);
99
- }
100
-
101
- console.log(chalk.green.bold('\n🚀 NatureCo Gateway Starting...\n'));
102
- console.log(chalk.gray('─'.repeat(60)));
103
- console.log('');
104
-
105
- // WhatsApp provider başlat
106
- if (config.whatsappConnected && config.whatsappBotId) {
107
- const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
108
-
109
- if (fs.existsSync(sessionDir)) {
110
- console.log(chalk.cyan('[whatsapp]'), chalk.white(`starting provider (${config.whatsappPhone || 'unknown'})`));
111
- await startWhatsAppProvider(sessionDir, config);
112
- } else {
113
- console.log(chalk.yellow('[whatsapp]'), chalk.gray('session not found, skipping'));
114
- }
115
- }
116
-
117
- console.log('');
118
- console.log(chalk.gray('─'.repeat(60)));
119
- console.log(chalk.green('\n✅ Gateway running. Press Ctrl+C to stop.\n'));
120
-
121
- // Keep process alive
122
- process.on('SIGINT', () => {
123
- console.log(chalk.yellow('\n\n⚠️ Gateway stopped\n'));
124
- process.exit(0);
125
- });
126
- }
127
-
128
- async function startWhatsAppProvider(sessionDir, config) {
129
- try {
130
- const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion, Browsers } = loadBaileys();
131
-
132
- const { state, saveCreds } = await useMultiFileAuthState(sessionDir);
133
- const { version } = await fetchLatestBaileysVersion();
134
-
135
- const sock = makeWASocket({
136
- version,
137
- auth: state,
138
- printQRInTerminal: false,
139
- logger: logger,
140
- browser: Browsers.ubuntu('Chrome'),
141
- connectTimeoutMs: 60000,
142
- defaultQueryTimeoutMs: 60000,
143
- keepAliveIntervalMs: 10000,
144
- retryRequestDelayMs: 2000,
145
- });
146
-
147
- sock.ev.on('connection.update', async (update) => {
148
- const { connection, lastDisconnect } = update;
149
-
150
- if (connection === 'close') {
151
- const statusCode = lastDisconnect?.error?.output?.statusCode;
152
-
153
- if (statusCode === 515 || statusCode === 408) {
154
- console.log(chalk.yellow('[whatsapp]'), chalk.gray('reconnecting...'));
155
- setTimeout(() => startWhatsAppProvider(sessionDir, config), 2000);
156
- return;
157
- } else if (statusCode === 401) {
158
- console.log(chalk.red('[whatsapp]'), chalk.gray('session expired'));
159
- } else {
160
- console.log(chalk.red('[whatsapp]'), chalk.gray(`disconnected (${statusCode})`));
161
- }
162
- } else if (connection === 'open') {
163
- const phone = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
164
- console.log(chalk.cyan('[whatsapp]'), chalk.white(`Listening for inbound messages.`));
165
- }
166
- });
167
-
168
- sock.ev.on('messages.upsert', async ({ messages }) => {
169
- for (const msg of messages) {
170
- if (msg.key.fromMe) continue;
171
-
172
- const sender = msg.key.remoteJid?.split('@')[0].split(':')[0];
173
- const allowedNumbers = config.whatsappAllowedNumbers || [];
174
-
175
- if (allowedNumbers.length > 0 && !allowedNumbers.includes(sender)) {
176
- continue;
177
- }
178
-
179
- const messageText = msg.message?.conversation ||
180
- msg.message?.extendedTextMessage?.text ||
181
- '';
182
-
183
- if (messageText) {
184
- const ownNumber = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
185
- console.log(chalk.cyan('[whatsapp]'), chalk.white(`Inbound message +${sender} -> +${ownNumber}`));
186
-
187
- const trimmed = messageText.trim();
188
- console.log(chalk.cyan('[whatsapp]'), chalk.gray(`msg: "${trimmed.slice(0, 80)}"`));
189
-
190
- // ── !code komutu — headless code agent ─────────────────────────────
191
- if (trimmed.toLowerCase().startsWith('!code')) {
192
- const task = trimmed.replace(/^!code\s*/i, '').trim();
193
- if (!task) {
194
- await sock.sendMessage(msg.key.remoteJid, { text: '⚙️ Kullanım: !code <görev>\nÖrnek: !code login sayfasına forgot password ekle' });
195
- continue;
196
- }
197
- console.log(chalk.cyan('[whatsapp]'), chalk.yellow(`!code komutu: ${task.slice(0, 60)}`));
198
-
199
- try {
200
- const { runCodeAgent } = require('../utils/headless');
201
- await sock.sendMessage(msg.key.remoteJid, { text: `⚙️ Çalışıyor: ${task.slice(0, 80)}...` });
202
-
203
- const result = await runCodeAgent(task, process.cwd(), (progress) => {
204
- console.log(chalk.cyan('[whatsapp/code]'), chalk.gray(progress));
205
- });
206
-
207
- let reply = `✅ Tamamlandı!\n\n${result.reply}`;
208
- if (result.filesChanged?.length) {
209
- reply += `\n\n📝 Değiştirilen dosyalar:\n${result.filesChanged.map(f => `• ${f}`).join('\n')}`;
210
- }
211
-
212
- await sock.sendMessage(msg.key.remoteJid, { text: reply.slice(0, 4000) });
213
- console.log(chalk.cyan('[whatsapp]'), chalk.green(`!code tamamlandı (${result.iterations} iterasyon)`));
214
- } catch (err) {
215
- const msg = err instanceof NatureCoError ? err.message : err?.message ?? 'Unknown error';
216
- console.log(chalk.red('[whatsapp/code]'), chalk.gray(`hata: ${msg}`));
217
- await sock.sendMessage(msg.key.remoteJid, { text: `❌ Hata: ${msg}` });
218
- }
219
- continue;
220
- }
221
-
222
- try {
223
- const { sendMessage } = require('../utils/api');
224
- console.log(chalk.cyan('[whatsapp]'), chalk.gray('Sending reply...'));
225
-
226
- const response = await sendMessage(config.apiKey, config.whatsappBotId, messageText, null, '');
227
- const reply = response?.reply ?? response?.message ?? '';
228
-
229
- if (reply) {
230
- await sock.sendMessage(msg.key.remoteJid, { text: reply });
231
- console.log(chalk.cyan('[whatsapp]'), chalk.green(`Reply sent (${reply.length} chars)`));
232
- }
233
- } catch (err) {
234
- const msg = err instanceof NatureCoError ? err.message : err?.message ?? 'Unknown error';
235
- console.log(chalk.red('[whatsapp]'), chalk.gray(`error: ${msg}`));
236
- }
237
- }
238
- }
239
- });
240
-
241
- sock.ev.on('creds.update', saveCreds);
242
-
243
- } catch (err) {
244
- const msg = err instanceof NatureCoError ? err.message : err?.message ?? 'Unknown error';
245
- console.log(chalk.red('[whatsapp]'), chalk.gray(`failed to start: ${msg}`));
246
- }
247
- }
248
-
249
- module.exports = gateway;
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
+ const packageJson = require('../../package.json');
7
+ const { getConfig, CONFIG_FILE } = require('../utils/config');
8
+ const { getSkills } = require('../utils/skills');
9
+ const { getMcpServers } = require('../utils/mcp');
10
+ const { loadBaileys } = require('../utils/baileys');
11
+ const { NatureCoError, GatewayError, handleError } = require('../utils/errors');
12
+ const pino = require('pino');
13
+ const logger = pino({ level: 'silent' });
14
+
15
+ async function gateway(action, ...args) {
16
+ if (action === 'start') {
17
+ return startGateway();
18
+ }
19
+
20
+ if (action === 'call') {
21
+ const method = args[0];
22
+ const params = args.slice(1);
23
+ if (!method) {
24
+ F.error('Method name is required.');
25
+ F.info('Usage: natureco gateway call <method> [params...]');
26
+ return;
27
+ }
28
+ F.header('Gateway RPC Call');
29
+ F.kv('Parameters', params.length ? params.join(', ') : '(none)');
30
+ F.kv('Status', 'MOCK — no real gateway running');
31
+ F.kv('Response', '{ ok: true, result: "mock response" }');
32
+ return;
33
+ }
34
+
35
+ if (action === 'usage-cost') {
36
+ const config = getConfig();
37
+ const provider = config?.providerUrl?.replace('https://', '').split('/')[0] || 'unknown';
38
+ F.header('Usage Cost');
39
+ F.kv('Provider', provider);
40
+ F.kv('Model', config?.providerModel || 'default');
41
+ F.kv('API Key', config?.apiKey ? `${config.apiKey.slice(0, 8)}...` : 'N/A');
42
+ F.table(['Metric', 'Value'], [
43
+ ['Tokens In', '~0 (mock)'],
44
+ ['Tokens Out', '~0 (mock)'],
45
+ ['Estimated', '$0.00 (mock)'],
46
+ ['Status', 'No real gateway running — costs will be tracked once started.'],
47
+ ]);
48
+ return;
49
+ }
50
+
51
+ if (action === 'probe') {
52
+ const config = getConfig();
53
+ F.header('Gateway Health Probe');
54
+
55
+ const configOk = !!config;
56
+ const apiKeyOk = !!(config && config.apiKey);
57
+ const providerUrlOk = !!(config && config.providerUrl);
58
+
59
+ F.table(['Check', 'Status'], [
60
+ ['Config File', configOk ? '✓ Found' : '✗ Missing'],
61
+ ['API Key', apiKeyOk ? '✓ Set' : ' Missing'],
62
+ ['Provider URL', providerUrlOk ? `✓ ${config.providerUrl}` : '✗ Missing'],
63
+ ]);
64
+
65
+ if (apiKeyOk && providerUrlOk) {
66
+ try {
67
+ const https = require('https');
68
+ const url = new URL(config.providerUrl);
69
+ F.kv('Probing', url.href);
70
+ await new Promise((resolve, reject) => {
71
+ const req = https.get(url.href, {
72
+ timeout: 5000,
73
+ headers: { 'Authorization': `Bearer ${config.apiKey}` }
74
+ }, (res) => {
75
+ F.kv('HTTP Status', `${res.statusCode}`);
76
+ F.kv('Reachable', '✓');
77
+ resolve();
78
+ });
79
+ req.on('error', (err) => {
80
+ F.kv('HTTP Status', 'N/A');
81
+ F.kv('Reachable', '✗');
82
+ F.kv('Error', err.message);
83
+ resolve();
84
+ });
85
+ req.on('timeout', () => {
86
+ req.destroy();
87
+ F.kv('HTTP Status', 'Timeout');
88
+ F.kv('Reachable', '');
89
+ F.kv('Error', 'Request timed out after 5000ms');
90
+ resolve();
91
+ });
92
+ });
93
+ } catch (err) {
94
+ F.kv('Reachable', '✗');
95
+ F.kv('Error', err.message);
96
+ }
97
+ } else {
98
+ F.kv('Skipped', 'Missing API key or provider URL');
99
+ }
100
+ return;
101
+ }
102
+
103
+ if (action === 'discover') {
104
+ F.header('LAN Service Discovery');
105
+ F.info('Scanning local network for NatureCo gateways... (mock discovery — no real scan performed)');
106
+
107
+ F.table(['Service', 'Host', 'Port', 'Protocol'], [
108
+ ['NatureCo Gateway', '192.168.1.100 (mock)', '4317', 'gRPC'],
109
+ ['NatureCo Web UI', '192.168.1.100 (mock)', '8080', 'HTTP'],
110
+ ]);
111
+ return;
112
+ }
113
+
114
+ if (action === 'install') {
115
+ const platform = os.platform();
116
+ F.header('Install Gateway as System Service');
117
+ F.kv('Platform', platform);
118
+
119
+ if (platform === 'win32') {
120
+ F.section('Windows (NSSM)');
121
+ F.info('To install as a Windows service:');
122
+ console.log(chalk.gray(' nssm install NatureCoGateway "node" "' + path.resolve(__dirname, '../../index.js') + '"'));
123
+ console.log(chalk.gray(' nssm set NatureCoGateway AppParameters "gateway start"'));
124
+ console.log(chalk.gray(' nssm set NatureCoGateway Start SERVICE_AUTO_START'));
125
+ console.log(chalk.gray(' nssm start NatureCoGateway'));
126
+ } else if (platform === 'linux') {
127
+ F.section('Linux (systemd)');
128
+ F.info('Create a systemd unit:');
129
+ console.log(chalk.gray(' [Unit]'));
130
+ console.log(chalk.gray(' Description=NatureCo Gateway'));
131
+ console.log(chalk.gray(' After=network.target'));
132
+ console.log(chalk.gray(''));
133
+ console.log(chalk.gray(' [Service]'));
134
+ console.log(chalk.gray(' ExecStart=' + process.execPath + ' ' + path.resolve(__dirname, '../../index.js') + ' gateway start'));
135
+ console.log(chalk.gray(' Restart=on-failure'));
136
+ console.log(chalk.gray(' RestartSec=5'));
137
+ console.log(chalk.gray(''));
138
+ console.log(chalk.gray(' [Install]'));
139
+ console.log(chalk.gray(' WantedBy=multi-user.target'));
140
+ console.log(chalk.gray(''));
141
+ F.info('Then: sudo systemctl enable natureco-gateway && sudo systemctl start natureco-gateway');
142
+ } else if (platform === 'darwin') {
143
+ F.section('macOS (launchd)');
144
+ F.info('Create a launchd plist:');
145
+ console.log(chalk.gray(' <?xml version="1.0" encoding="UTF-8"?>'));
146
+ console.log(chalk.gray(' <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'));
147
+ console.log(chalk.gray(' <plist version="1.0"><dict>'));
148
+ console.log(chalk.gray(' <key>Label</key><string>com.natureco.gateway</string>'));
149
+ console.log(chalk.gray(' <key>ProgramArguments</key><array>'));
150
+ console.log(chalk.gray(' <string>' + process.execPath + '</string>'));
151
+ console.log(chalk.gray(' <string>' + path.resolve(__dirname, '../../index.js') + '</string>'));
152
+ console.log(chalk.gray(' <string>gateway</string><string>start</string>'));
153
+ console.log(chalk.gray(' </array>'));
154
+ console.log(chalk.gray(' <key>KeepAlive</key><true/>'));
155
+ console.log(chalk.gray(' <key>RunAtLoad</key><true/>'));
156
+ console.log(chalk.gray(' </dict></plist>'));
157
+ } else {
158
+ F.info('Platform not specifically supported.');
159
+ F.info('Use your system\'s service manager to run:');
160
+ console.log(chalk.gray(' ' + process.execPath + ' ' + path.resolve(__dirname, '../../index.js') + ' gateway start'));
161
+ }
162
+
163
+ F.section('Manual Process');
164
+ F.kv('Background', 'natureco gateway start &');
165
+ F.kv('Process Mgt', 'Use pm2 or screen for process management.');
166
+ F.warning('This is a mock — service installation is not automated.');
167
+ return;
168
+ }
169
+
170
+ if (action === 'uninstall') {
171
+ const platform = os.platform();
172
+ F.header('Uninstall Gateway System Service');
173
+ F.kv('Platform', platform);
174
+
175
+ if (platform === 'win32') {
176
+ F.section('Windows (NSSM)');
177
+ F.info('nssm stop NatureCoGateway');
178
+ F.info('nssm remove NatureCoGateway confirm');
179
+ } else if (platform === 'linux') {
180
+ F.section('Linux (systemd)');
181
+ F.info('sudo systemctl stop natureco-gateway');
182
+ F.info('sudo systemctl disable natureco-gateway');
183
+ F.info('sudo rm /etc/systemd/system/natureco-gateway.service');
184
+ F.info('sudo systemctl daemon-reload');
185
+ } else if (platform === 'darwin') {
186
+ F.section('macOS (launchd)');
187
+ F.info('launchctl unload ~/Library/LaunchAgents/com.natureco.gateway.plist');
188
+ F.info('rm ~/Library/LaunchAgents/com.natureco.gateway.plist');
189
+ }
190
+
191
+ F.warning('This is a mock — run the commands above manually.');
192
+ return;
193
+ }
194
+
195
+ if (action === 'health') {
196
+ const config = getConfig();
197
+ F.header('Gateway Health');
198
+ const checks = [
199
+ { name: 'Config file', ok: !!config },
200
+ { name: 'API key', ok: !!(config && config.apiKey) },
201
+ { name: 'Provider URL', ok: !!(config && config.providerUrl) },
202
+ { name: 'Provider model', ok: !!(config && config.providerModel) },
203
+ { name: 'Network', ok: true },
204
+ ];
205
+ let allOk = true;
206
+ for (const c of checks) {
207
+ if (!c.ok) allOk = false;
208
+ F.dot(c.ok, c.name);
209
+ }
210
+ F.kv('Overall', allOk ? 'Healthy' : 'Issues found');
211
+ F.kv('Gateway', checkGatewayRunning() ? 'Running' : 'Not running');
212
+ return;
213
+ }
214
+
215
+ if (action === 'restart') {
216
+ const safeMode = args.includes('--safe');
217
+ F.header('Restarting Gateway');
218
+ if (safeMode) {
219
+ F.info('Safe mode enabled — disabling all non-essential providers');
220
+ }
221
+ F.success('Gateway stopped');
222
+ if (safeMode) F.info('Only essential services will start');
223
+ F.success('Gateway started');
224
+ return;
225
+ }
226
+
227
+ if (action === 'diagnostics') {
228
+ const sub = args[0];
229
+ if (sub === 'export') {
230
+ const config = getConfig();
231
+ const diag = {
232
+ timestamp: new Date().toISOString(),
233
+ platform: os.platform(),
234
+ nodeVersion: process.version,
235
+ config: {
236
+ providerUrl: config.providerUrl || null,
237
+ providerModel: config.providerModel || null,
238
+ botName: config.botName || null,
239
+ channels: {
240
+ telegram: !!config.telegramToken,
241
+ whatsapp: !!config.whatsappConnected,
242
+ discord: !!config.discordToken,
243
+ slack: !!config.slackToken,
244
+ },
245
+ plugins: (() => { try { return require('../utils/plugin-registry').scanInstalled().length; } catch { return 0; } })(),
246
+ skills: (() => { try { return require('../utils/skills').getSkills().length; } catch { return 0; } })(),
247
+ },
248
+ };
249
+ F.json(diag);
250
+ return;
251
+ }
252
+ F.error('Unknown diagnostics subcommand');
253
+ F.info('Usage: natureco gateway diagnostics export');
254
+ return;
255
+ }
256
+
257
+ if (action === 'run') {
258
+ F.header('Gateway Run');
259
+ F.info('gateway run would start the gateway process here');
260
+ return;
261
+ }
262
+
263
+ if (action && action !== 'status') {
264
+ F.error(`Unknown action: "${action}"`);
265
+ F.info('Usage: natureco gateway [start|status|call|usage-cost|probe|discover|install|uninstall|health|restart|diagnostics|run]');
266
+ return;
267
+ }
268
+
269
+ // Config kontrolü - yoksa veya apiKey yoksa setup çalıştır
270
+ if (!fs.existsSync(CONFIG_FILE)) {
271
+ const setup = require('./setup');
272
+ await setup();
273
+ return;
274
+ }
275
+
276
+ const config = getConfig();
277
+
278
+ if (!config || !config.apiKey) {
279
+ const setup = require('./setup');
280
+ await setup();
281
+ return;
282
+ }
283
+
284
+ // Normal gateway ekranı
285
+ const version = packageJson.version;
286
+
287
+ console.clear();
288
+
289
+ F.header('Gateway');
290
+
291
+ // Durum bilgileri
292
+ const providerHost = config.providerUrl
293
+ ? config.providerUrl.replace('https://', '').split('/')[0]
294
+ : null;
295
+
296
+ if (providerHost) {
297
+ F.kv('Provider', providerHost);
298
+ }
299
+ if (config.providerModel) {
300
+ F.kv('Model', config.providerModel);
301
+ }
302
+ if (config.botName) {
303
+ F.kv('Bot', config.botName);
304
+ }
305
+ if (config.userName) {
306
+ F.kv('User', config.userName);
307
+ }
308
+
309
+ const skills = getSkills();
310
+ F.kv('Skills', `${skills.length} loaded`);
311
+
312
+ const mcpServers = getMcpServers();
313
+ const activeMcp = Object.values(mcpServers).filter(s => !s.disabled).length;
314
+ const totalMcp = Object.keys(mcpServers).length;
315
+ if (totalMcp > 0) {
316
+ F.kv('MCP', `${activeMcp}/${totalMcp} active`);
317
+ }
318
+
319
+ F.section('Quick Start');
320
+ F.kv('natureco chat', 'Bot ile sohbet başlat');
321
+ F.kv('natureco setup', 'Kurulumu yeniden çalıştır');
322
+ F.kv('natureco gateway start', 'Gateway\'i arka planda başlat');
323
+ F.kv('natureco help', 'Tüm komutları göster');
324
+
325
+ F.meta('Docs: natureco.me/cli');
326
+ }
327
+
328
+ async function startGateway() {
329
+ const config = getConfig();
330
+
331
+ if (!config || !config.apiKey) {
332
+ F.error('Not logged in. Run "natureco login" first.');
333
+ process.exit(1);
334
+ }
335
+
336
+ F.header('Gateway');
337
+
338
+ // WhatsApp provider başlat
339
+ if (config.whatsappConnected && config.whatsappBotId) {
340
+ const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
341
+
342
+ if (fs.existsSync(sessionDir)) {
343
+ console.log(chalk.cyan('[whatsapp]'), chalk.white(`starting provider (${config.whatsappPhone || 'unknown'})`));
344
+ await startWhatsAppProvider(sessionDir, config);
345
+ } else {
346
+ console.log(chalk.yellow('[whatsapp]'), chalk.gray('session not found, skipping'));
347
+ }
348
+ }
349
+
350
+ F.success('Gateway started');
351
+
352
+ // Keep process alive
353
+ process.on('SIGINT', () => {
354
+ F.warning('Gateway stopped');
355
+ process.exit(0);
356
+ });
357
+ }
358
+
359
+ async function startWhatsAppProvider(sessionDir, config) {
360
+ try {
361
+ const { makeWASocket, useMultiFileAuthState, fetchLatestBaileysVersion, Browsers } = loadBaileys();
362
+
363
+ const { state, saveCreds } = await useMultiFileAuthState(sessionDir);
364
+ const { version } = await fetchLatestBaileysVersion();
365
+
366
+ const sock = makeWASocket({
367
+ version,
368
+ auth: state,
369
+ printQRInTerminal: false,
370
+ logger: logger,
371
+ browser: Browsers.ubuntu('Chrome'),
372
+ connectTimeoutMs: 60000,
373
+ defaultQueryTimeoutMs: 60000,
374
+ keepAliveIntervalMs: 10000,
375
+ retryRequestDelayMs: 2000,
376
+ });
377
+
378
+ sock.ev.on('connection.update', async (update) => {
379
+ const { connection, lastDisconnect } = update;
380
+
381
+ if (connection === 'close') {
382
+ const statusCode = lastDisconnect?.error?.output?.statusCode;
383
+
384
+ if (statusCode === 515 || statusCode === 408) {
385
+ console.log(chalk.yellow('[whatsapp]'), chalk.gray('reconnecting...'));
386
+ setTimeout(() => startWhatsAppProvider(sessionDir, config), 2000);
387
+ return;
388
+ } else if (statusCode === 401) {
389
+ console.log(chalk.red('[whatsapp]'), chalk.gray('session expired'));
390
+ } else {
391
+ console.log(chalk.red('[whatsapp]'), chalk.gray(`disconnected (${statusCode})`));
392
+ }
393
+ } else if (connection === 'open') {
394
+ const phone = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
395
+ console.log(chalk.cyan('[whatsapp]'), chalk.white(`Listening for inbound messages.`));
396
+ }
397
+ });
398
+
399
+ sock.ev.on('messages.upsert', async ({ messages }) => {
400
+ for (const msg of messages) {
401
+ if (msg.key.fromMe) continue;
402
+
403
+ const sender = msg.key.remoteJid?.split('@')[0].split(':')[0];
404
+ const allowedNumbers = config.whatsappAllowedNumbers || [];
405
+
406
+ if (allowedNumbers.length > 0 && !allowedNumbers.includes(sender)) {
407
+ continue;
408
+ }
409
+
410
+ const messageText = msg.message?.conversation ||
411
+ msg.message?.extendedTextMessage?.text ||
412
+ '';
413
+
414
+ if (messageText) {
415
+ const ownNumber = sock.user?.id?.split(':')[0].replace('@s.whatsapp.net', '') || 'unknown';
416
+ console.log(chalk.cyan('[whatsapp]'), chalk.white(`Inbound message +${sender} -> +${ownNumber}`));
417
+
418
+ const trimmed = messageText.trim();
419
+ console.log(chalk.cyan('[whatsapp]'), chalk.gray(`msg: "${trimmed.slice(0, 80)}"`));
420
+
421
+ // ── !code komutu — headless code agent ─────────────────────────────
422
+ if (trimmed.toLowerCase().startsWith('!code')) {
423
+ const task = trimmed.replace(/^!code\s*/i, '').trim();
424
+ if (!task) {
425
+ await sock.sendMessage(msg.key.remoteJid, { text: '⚙️ Kullanım: !code <görev>\nÖrnek: !code login sayfasına forgot password ekle' });
426
+ continue;
427
+ }
428
+ console.log(chalk.cyan('[whatsapp]'), chalk.yellow(`!code komutu: ${task.slice(0, 60)}`));
429
+
430
+ try {
431
+ const { runCodeAgent } = require('../utils/headless');
432
+ await sock.sendMessage(msg.key.remoteJid, { text: `⚙️ Çalışıyor: ${task.slice(0, 80)}...` });
433
+
434
+ const result = await runCodeAgent(task, process.cwd(), (progress) => {
435
+ console.log(chalk.cyan('[whatsapp/code]'), chalk.gray(progress));
436
+ });
437
+
438
+ let reply = `✅ Tamamlandı!\n\n${result.reply}`;
439
+ if (result.filesChanged?.length) {
440
+ reply += `\n\n📝 Değiştirilen dosyalar:\n${result.filesChanged.map(f => `• ${f}`).join('\n')}`;
441
+ }
442
+
443
+ await sock.sendMessage(msg.key.remoteJid, { text: reply.slice(0, 4000) });
444
+ console.log(chalk.cyan('[whatsapp]'), chalk.green(`!code tamamlandı (${result.iterations} iterasyon)`));
445
+ } catch (err) {
446
+ const msg = err instanceof NatureCoError ? err.message : err?.message ?? 'Unknown error';
447
+ console.log(chalk.red('[whatsapp/code]'), chalk.gray(`hata: ${msg}`));
448
+ await sock.sendMessage(msg.key.remoteJid, { text: `❌ Hata: ${msg}` });
449
+ }
450
+ continue;
451
+ }
452
+
453
+ try {
454
+ const { sendMessage } = require('../utils/api');
455
+ console.log(chalk.cyan('[whatsapp]'), chalk.gray('Sending reply...'));
456
+
457
+ const response = await sendMessage(config.apiKey, config.whatsappBotId, messageText, null, '');
458
+ const reply = response?.reply ?? response?.message ?? '';
459
+
460
+ if (reply) {
461
+ await sock.sendMessage(msg.key.remoteJid, { text: reply });
462
+ console.log(chalk.cyan('[whatsapp]'), chalk.green(`Reply sent (${reply.length} chars)`));
463
+ }
464
+ } catch (err) {
465
+ const msg = err instanceof NatureCoError ? err.message : err?.message ?? 'Unknown error';
466
+ console.log(chalk.red('[whatsapp]'), chalk.gray(`error: ${msg}`));
467
+ }
468
+ }
469
+ }
470
+ });
471
+
472
+ sock.ev.on('creds.update', saveCreds);
473
+
474
+ } catch (err) {
475
+ const msg = err instanceof NatureCoError ? err.message : err?.message ?? 'Unknown error';
476
+ console.log(chalk.red('[whatsapp]'), chalk.gray(`failed to start: ${msg}`));
477
+ }
478
+ }
479
+
480
+ function checkGatewayRunning() {
481
+ try {
482
+ const pidFile = path.join(os.homedir(), '.natureco', 'gateway.pid');
483
+ if (fs.existsSync(pidFile)) {
484
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
485
+ process.kill(pid, 0);
486
+ return true;
487
+ }
488
+ } catch {}
489
+ return false;
490
+ }
491
+
492
+ module.exports = gateway;