natureco-cli 2.23.28 → 2.23.30

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 (96) hide show
  1. package/README.md +94 -11
  2. package/bin/natureco.js +470 -10
  3. package/package.json +10 -6
  4. package/src/commands/admin-rpc.js +219 -0
  5. package/src/commands/agent.js +89 -0
  6. package/src/commands/approvals.js +53 -0
  7. package/src/commands/backup.js +124 -0
  8. package/src/commands/bonjour.js +167 -0
  9. package/src/commands/capability.js +64 -0
  10. package/src/commands/channels.js +94 -4
  11. package/src/commands/chat.js +11 -25
  12. package/src/commands/clickclack.js +130 -0
  13. package/src/commands/commitments.js +32 -0
  14. package/src/commands/completion.js +76 -0
  15. package/src/commands/config.js +111 -68
  16. package/src/commands/configure.js +93 -0
  17. package/src/commands/crestodian.js +92 -0
  18. package/src/commands/daemon.js +60 -0
  19. package/src/commands/device-pair.js +248 -0
  20. package/src/commands/devices.js +110 -0
  21. package/src/commands/directory.js +47 -0
  22. package/src/commands/dns.js +58 -0
  23. package/src/commands/docs.js +43 -0
  24. package/src/commands/doctor.js +121 -16
  25. package/src/commands/exec-policy.js +71 -0
  26. package/src/commands/gateway-server.js +1175 -30
  27. package/src/commands/gateway.js +11 -20
  28. package/src/commands/health.js +18 -0
  29. package/src/commands/help.js +6 -0
  30. package/src/commands/imessage.js +169 -0
  31. package/src/commands/infer.js +73 -0
  32. package/src/commands/irc.js +119 -0
  33. package/src/commands/mattermost.js +164 -0
  34. package/src/commands/memory-cmd.js +134 -1
  35. package/src/commands/message.js +30 -4
  36. package/src/commands/migrate.js +213 -2
  37. package/src/commands/models.js +584 -216
  38. package/src/commands/node.js +98 -0
  39. package/src/commands/nodes.js +106 -0
  40. package/src/commands/oc-path.js +200 -0
  41. package/src/commands/onboard.js +70 -0
  42. package/src/commands/open-prose.js +67 -0
  43. package/src/commands/plugins.js +415 -172
  44. package/src/commands/policy.js +176 -0
  45. package/src/commands/proxy.js +155 -0
  46. package/src/commands/qr.js +28 -0
  47. package/src/commands/sandbox.js +125 -0
  48. package/src/commands/secrets.js +118 -0
  49. package/src/commands/security.js +149 -1
  50. package/src/commands/setup.js +114 -10
  51. package/src/commands/signal.js +495 -0
  52. package/src/commands/skills.js +20 -29
  53. package/src/commands/sms.js +168 -0
  54. package/src/commands/system.js +53 -0
  55. package/src/commands/tasks.js +328 -79
  56. package/src/commands/terminal.js +21 -0
  57. package/src/commands/thread-ownership.js +157 -0
  58. package/src/commands/transcripts.js +72 -0
  59. package/src/commands/voice.js +82 -0
  60. package/src/commands/vydra.js +98 -0
  61. package/src/commands/webhooks.js +79 -0
  62. package/src/commands/whatsapp.js +7 -21
  63. package/src/commands/workboard.js +207 -0
  64. package/src/tools/audio_understanding.js +154 -0
  65. package/src/tools/bash.js +63 -29
  66. package/src/tools/browser.js +112 -0
  67. package/src/tools/canvas.js +104 -0
  68. package/src/tools/document_extract.js +84 -0
  69. package/src/tools/duckduckgo.js +54 -0
  70. package/src/tools/exa_search.js +66 -0
  71. package/src/tools/firecrawl.js +104 -0
  72. package/src/tools/image_generation.js +99 -0
  73. package/src/tools/llm_task.js +118 -0
  74. package/src/tools/media_understanding.js +128 -0
  75. package/src/tools/music_generation.js +113 -0
  76. package/src/tools/parallel_search.js +77 -0
  77. package/src/tools/phone_control.js +80 -0
  78. package/src/tools/phone_control_enhanced.js +184 -0
  79. package/src/tools/searxng.js +61 -0
  80. package/src/tools/speech_to_text.js +135 -0
  81. package/src/tools/text_to_speech.js +105 -0
  82. package/src/tools/thread_ownership.js +88 -0
  83. package/src/tools/video_generation.js +72 -0
  84. package/src/tools/web_readability.js +104 -0
  85. package/src/utils/api.js +3 -20
  86. package/src/utils/approvals.js +297 -0
  87. package/src/utils/background.js +223 -66
  88. package/src/utils/baileys.js +21 -0
  89. package/src/utils/config.js +141 -10
  90. package/src/utils/errors.js +148 -0
  91. package/src/utils/inquirer-wrapper.js +1 -2
  92. package/src/utils/memory.js +200 -0
  93. package/src/utils/path-utils.js +13 -13
  94. package/src/utils/plugin-registry.js +238 -0
  95. package/src/utils/secrets.js +177 -0
  96. package/src/utils/skills.js +10 -23
@@ -0,0 +1,495 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('../utils/inquirer-wrapper');
3
+ const { getConfig, saveConfig } = require('../utils/config');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+ const { spawn, execSync } = require('child_process');
8
+
9
+ const SIGNAL_DIR = path.join(os.homedir(), '.natureco', 'signal');
10
+ const SIGNAL_PID_FILE = path.join(SIGNAL_DIR, 'daemon.pid');
11
+ const SIGNAL_LOG_FILE = path.join(SIGNAL_DIR, 'daemon.log');
12
+ const SIGNAL_CLI_VERSION = '0.13.6';
13
+
14
+ async function signal(action, subAction) {
15
+ if (!action || action === 'connect') return connectSignal();
16
+ if (action === 'disconnect') return disconnectSignal();
17
+ if (action === 'status') return statusSignal();
18
+ if (action === 'install') return installSignalCli();
19
+ if (action === 'daemon') return manageDaemon(subAction);
20
+ if (action === 'probe') return probeSignalCli();
21
+ console.log(chalk.red('\n❌ Unknown action\n'));
22
+ console.log(chalk.gray('Available actions: connect, disconnect, status, install, daemon, probe\n'));
23
+ process.exit(1);
24
+ }
25
+
26
+ async function connectSignal() {
27
+ const config = getConfig();
28
+ if (!config.providerUrl) {
29
+ console.log(chalk.red('\n❌ Setup yapılmamış. Önce "natureco setup" çalıştırın.\n'));
30
+ process.exit(1);
31
+ }
32
+ console.log(chalk.yellow('\n⏳ Signal bağlantısı hazırlanıyor...\n'));
33
+ console.log(chalk.gray('Signal, signal-cli REST API üzerinden bağlanır.\n'));
34
+
35
+ const existing = config.signalHttpUrl || '';
36
+ const existingAccount = config.signalAccount || '';
37
+ const existingDm = config.signalDmPolicy || 'pairing';
38
+ const existingMode = config.signalApiMode || 'auto';
39
+
40
+ const answers = await inquirer.prompt([
41
+ { type: 'input', name: 'httpUrl', message: 'signal-cli REST API URL:', default: existing || 'http://localhost:8080', validate: v => v.trim() ? true : 'URL boş olamaz' },
42
+ { type: 'input', name: 'account', message: 'Signal hesap numarası (+905551234567):', default: existingAccount, validate: v => v.trim() ? true : 'Numara boş olamaz' },
43
+ {
44
+ type: 'list', name: 'apiMode', message: 'API modu:', default: existingMode,
45
+ choices: [
46
+ { name: 'Auto-detect (önerilen)', value: 'auto' },
47
+ { name: 'Native (signal-cli JSON-RPC)', value: 'native' },
48
+ { name: 'Container (bbernhard/signal-cli-rest-api)', value: 'container' },
49
+ ]
50
+ },
51
+ { type: 'confirm', name: 'autoStart', message: 'Gateway ile signal-cli daemon\'ı otomatik başlatılsın mı?', default: config.signalAutoStart !== undefined ? config.signalAutoStart : true },
52
+ {
53
+ type: 'list', name: 'receiveMode', message: 'Mesaj alma modu:', default: config.signalReceiveMode || 'on-start',
54
+ choices: [
55
+ { name: 'Gateway başlangıcında dinlemeye başla (önerilen)', value: 'on-start' },
56
+ { name: 'Manuel (sadece status kontrol)', value: 'manual' },
57
+ ]
58
+ },
59
+ { type: 'list', name: 'dmPolicy', message: 'DM politikası:', default: existingDm, choices: [
60
+ { name: 'Pairing (önerilen) — eşleşme onayı ile', value: 'pairing' },
61
+ { name: 'Allowlist — sadece izin listesi', value: 'allowlist' },
62
+ { name: 'Open — herkese açık', value: 'open' },
63
+ { name: 'Disabled — devre dışı', value: 'disabled' },
64
+ ]},
65
+ ]);
66
+
67
+ const botId = `signal_${Date.now()}`;
68
+ config.signalHttpUrl = answers.httpUrl.trim().replace(/\/+$/, '');
69
+ config.signalAccount = answers.account.trim();
70
+ config.signalApiMode = answers.apiMode;
71
+ config.signalAutoStart = answers.autoStart;
72
+ config.signalReceiveMode = answers.receiveMode;
73
+ config.signalDmPolicy = answers.dmPolicy;
74
+ config.signalBotId = botId;
75
+ saveConfig(config);
76
+
77
+ console.log(chalk.green('\n✅ Signal bağlantısı kaydedildi!\n'));
78
+ console.log(chalk.cyan('Bot ID:'), chalk.white(botId));
79
+ console.log(chalk.cyan('REST API URL:'), chalk.white(config.signalHttpUrl));
80
+ console.log(chalk.cyan('Hesap:'), chalk.white(config.signalAccount));
81
+ console.log(chalk.cyan('API Modu:'), chalk.white(answers.apiMode));
82
+ console.log(chalk.cyan('DM Politikası:'), chalk.white(answers.dmPolicy));
83
+
84
+ if (answers.autoStart) {
85
+ const signalCliPath = findSignalCli();
86
+ if (signalCliPath) {
87
+ console.log(chalk.gray(`\nsignal-cli bulundu: ${signalCliPath}`));
88
+ } else {
89
+ console.log(chalk.yellow('\n⚠️ signal-cli bulunamadı. İndirmek için:'));
90
+ console.log(chalk.cyan(' natureco signal install'));
91
+ }
92
+ }
93
+ console.log(chalk.gray('\nGateway ile başlatmak için: natureco gateway start\n'));
94
+ }
95
+
96
+ async function disconnectSignal() {
97
+ const config = getConfig();
98
+ if (!config.signalBotId) {
99
+ console.log(chalk.gray('\n⚠️ No Signal connection found\n'));
100
+ return;
101
+ }
102
+ const { confirm } = await inquirer.prompt([
103
+ { type: 'confirm', name: 'confirm', message: 'Signal bağlantısını kaldırmak istediğinize emin misiniz?', default: false }
104
+ ]);
105
+ if (!confirm) {
106
+ console.log(chalk.gray('\nCancelled\n'));
107
+ return;
108
+ }
109
+ // Stop daemon if running
110
+ stopDaemonProcess();
111
+
112
+ delete config.signalHttpUrl;
113
+ delete config.signalAccount;
114
+ delete config.signalApiMode;
115
+ delete config.signalAutoStart;
116
+ delete config.signalReceiveMode;
117
+ delete config.signalDmPolicy;
118
+ delete config.signalBotId;
119
+ saveConfig(config);
120
+ console.log(chalk.green('\n✅ Signal disconnected\n'));
121
+ }
122
+
123
+ function statusSignal() {
124
+ const config = getConfig();
125
+ if (!config.signalBotId) {
126
+ console.log(chalk.gray('\n⚠️ Signal not connected\n'));
127
+ console.log(chalk.gray('Connect with: natureco signal connect\n'));
128
+ return;
129
+ }
130
+
131
+ const daemonRunning = isDaemonRunning();
132
+ const apiReachable = probeSync(config);
133
+
134
+ console.log(chalk.green('\n✅ Signal connected\n'));
135
+ console.log(chalk.cyan('Bot ID:'), chalk.white(config.signalBotId));
136
+ console.log(chalk.cyan('REST API URL:'), chalk.white(config.signalHttpUrl));
137
+ console.log(chalk.cyan('Hesap:'), chalk.white(config.signalAccount));
138
+ console.log(chalk.cyan('API Modu:'), chalk.white(config.signalApiMode || 'auto'));
139
+ console.log(chalk.cyan('DM Politikası:'), chalk.white(config.signalDmPolicy || 'pairing'));
140
+
141
+ console.log('');
142
+ console.log(chalk.cyan('Daemon Durumu:'), daemonRunning ? chalk.green('✓ Çalışıyor') : chalk.gray('✗ Durduruldu'));
143
+ console.log(chalk.cyan('API Erişilebilirlik:'), apiReachable ? chalk.green('✓ Ulaşılabilir') : chalk.red('✗ Ulaşılamıyor'));
144
+
145
+ if (config.signalCliPath) {
146
+ console.log(chalk.gray(`\nsignal-cli: ${config.signalCliPath}`));
147
+ }
148
+ const cliPath = findSignalCli();
149
+ if (cliPath) {
150
+ console.log(chalk.gray(`signal-cli binary: ${cliPath}`));
151
+ }
152
+ console.log(chalk.gray('\nActions: connect, disconnect, install, daemon start/stop/status, probe\n'));
153
+ }
154
+
155
+ async function installSignalCli() {
156
+ console.log(chalk.yellow('\n⏳ signal-cli indiriliyor...\n'));
157
+
158
+ if (!fs.existsSync(SIGNAL_DIR)) {
159
+ fs.mkdirSync(SIGNAL_DIR, { recursive: true });
160
+ }
161
+
162
+ const platform = process.platform;
163
+ let downloadUrl;
164
+ let archiveName;
165
+
166
+ if (platform === 'win32') {
167
+ archiveName = `signal-cli-${SIGNAL_CLI_VERSION}-Windows.tar.gz`;
168
+ downloadUrl = `https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/${archiveName}`;
169
+ } else if (platform === 'linux') {
170
+ archiveName = `signal-cli-${SIGNAL_CLI_VERSION}-Linux.tar.gz`;
171
+ downloadUrl = `https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/${archiveName}`;
172
+ } else if (platform === 'darwin') {
173
+ archiveName = `signal-cli-${SIGNAL_CLI_VERSION}-macOS.tar.gz`;
174
+ downloadUrl = `https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/${archiveName}`;
175
+ } else {
176
+ console.log(chalk.red(`\n❌ Platform desteklenmiyor: ${platform}\n`));
177
+ console.log(chalk.gray('Manual kurulum: https://github.com/AsamK/signal-cli#installation\n'));
178
+ process.exit(1);
179
+ }
180
+
181
+ const archivePath = path.join(SIGNAL_DIR, archiveName);
182
+ const extractDir = path.join(SIGNAL_DIR, `signal-cli-${SIGNAL_CLI_VERSION}`);
183
+
184
+ if (fs.existsSync(extractDir)) {
185
+ console.log(chalk.gray(`signal-cli ${SIGNAL_CLI_VERSION} zaten indirilmiş: ${extractDir}`));
186
+ const config = getConfig();
187
+ config.signalCliPath = path.join(extractDir, 'bin', 'signal-cli' + (platform === 'win32' ? '.bat' : ''));
188
+ saveConfig(config);
189
+ console.log(chalk.green('✅ Hazır\n'));
190
+ return;
191
+ }
192
+
193
+ try {
194
+ console.log(chalk.cyan(`İndiriliyor: ${downloadUrl}`));
195
+ const response = await fetch(downloadUrl);
196
+ if (!response.ok) {
197
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
198
+ }
199
+ const buffer = Buffer.from(await response.arrayBuffer());
200
+ fs.writeFileSync(archivePath, buffer);
201
+ console.log(chalk.green(`✓ İndirildi (${(buffer.length / 1024 / 1024).toFixed(1)} MB)`));
202
+
203
+ // Extract
204
+ console.log(chalk.cyan('Ayıklanıyor...'));
205
+ const tar = require('tar');
206
+ fs.mkdirSync(extractDir, { recursive: true });
207
+ await tar.x({ file: archivePath, cwd: SIGNAL_DIR });
208
+ console.log(chalk.green('✓ Ayıklandı'));
209
+
210
+ // Clean up archive
211
+ fs.unlinkSync(archivePath);
212
+
213
+ // Make executable on Unix
214
+ if (platform !== 'win32') {
215
+ const binaryPath = path.join(extractDir, 'bin', 'signal-cli');
216
+ if (fs.existsSync(binaryPath)) {
217
+ fs.chmodSync(binaryPath, 0o755);
218
+ }
219
+ }
220
+
221
+ // Save path to config
222
+ const config = getConfig();
223
+ config.signalCliPath = path.join(extractDir, 'bin', 'signal-cli' + (platform === 'win32' ? '.bat' : ''));
224
+ saveConfig(config);
225
+
226
+ console.log(chalk.green(`\n✅ signal-cli ${SIGNAL_CLI_VERSION} kuruldu!\n`));
227
+ console.log(chalk.cyan('Binary:'), chalk.white(config.signalCliPath));
228
+ console.log(chalk.gray('\nDaemon\'ı başlatmak için: natureco signal daemon start\n'));
229
+
230
+ } catch (err) {
231
+ console.log(chalk.red(`\n❌ Kurulum hatası: ${err.message}\n`));
232
+ console.log(chalk.gray('Manuel kurulum: https://github.com/AsamK/signal-cli#installation\n'));
233
+ process.exit(1);
234
+ }
235
+ }
236
+
237
+ async function manageDaemon(subAction) {
238
+ const config = getConfig();
239
+ if (!config.signalBotId) {
240
+ console.log(chalk.red('\n❌ Signal bağlı değil\n'));
241
+ process.exit(1);
242
+ }
243
+
244
+ if (subAction === 'start') {
245
+ return startDaemon(config);
246
+ } else if (subAction === 'stop') {
247
+ return stopDaemon();
248
+ } else if (subAction === 'status') {
249
+ return daemonStatus();
250
+ } else {
251
+ console.log(chalk.red('\n❌ Unknown daemon action\n'));
252
+ console.log(chalk.gray('Usage: natureco signal daemon <start|stop|status>\n'));
253
+ process.exit(1);
254
+ }
255
+ }
256
+
257
+ async function startDaemon(config) {
258
+ if (isDaemonRunning()) {
259
+ console.log(chalk.yellow('\n⚠️ Daemon already running\n'));
260
+ console.log(chalk.cyan('PID:'), chalk.white(fs.readFileSync(SIGNAL_PID_FILE, 'utf-8').trim()));
261
+ return;
262
+ }
263
+
264
+ const cliPath = config.signalCliPath || findSignalCli();
265
+ if (!cliPath) {
266
+ console.log(chalk.red('\n❌ signal-cli bulunamadı\n'));
267
+ console.log(chalk.yellow('Önce indirin:'), chalk.cyan('natureco signal install\n'));
268
+ process.exit(1);
269
+ }
270
+
271
+ if (!fs.existsSync(cliPath)) {
272
+ console.log(chalk.red(`\n❌ signal-cli binary bulunamadı: ${cliPath}\n`));
273
+ process.exit(1);
274
+ }
275
+
276
+ if (!fs.existsSync(SIGNAL_DIR)) {
277
+ fs.mkdirSync(SIGNAL_DIR, { recursive: true });
278
+ }
279
+
280
+ const httpUrl = config.signalHttpUrl || 'http://localhost:8080';
281
+ const urlObj = new URL(httpUrl);
282
+ const host = urlObj.hostname || '127.0.0.1';
283
+ const port = urlObj.port || '8080';
284
+ const account = config.signalAccount;
285
+
286
+ console.log(chalk.yellow(`\n⏳ signal-cli daemon başlatılıyor...\n`));
287
+ console.log(chalk.gray(`HTTP: ${host}:${port}`));
288
+ console.log(chalk.gray(`Hesap: ${account}`));
289
+
290
+ const args = [
291
+ 'daemon',
292
+ '--http', `${host}:${port}`,
293
+ '--no-receive-stdout',
294
+ ];
295
+ if (account) {
296
+ args.push('-a', account);
297
+ }
298
+
299
+ const logFd = fs.openSync(SIGNAL_LOG_FILE, 'a');
300
+ const child = spawn(cliPath, args, {
301
+ detached: true,
302
+ stdio: ['ignore', logFd, logFd],
303
+ windowsHide: true,
304
+ });
305
+
306
+ child.unref();
307
+ fs.writeFileSync(SIGNAL_PID_FILE, String(child.pid), 'utf-8');
308
+
309
+ console.log(chalk.green('✅ Daemon başlatıldı'));
310
+ console.log(chalk.cyan('PID:'), chalk.white(child.pid));
311
+ console.log(chalk.cyan('Log:'), chalk.white(SIGNAL_LOG_FILE));
312
+
313
+ // Wait for API to be ready
314
+ console.log(chalk.gray('\nAPI hazır olana kadar bekleniyor...'));
315
+ for (let i = 0; i < 30; i++) {
316
+ await new Promise(r => setTimeout(r, 1000));
317
+ if (probeSync(config)) {
318
+ console.log(chalk.green('✓ API hazır\n'));
319
+ return;
320
+ }
321
+ process.stdout.write('.');
322
+ }
323
+ console.log(chalk.yellow('\n⚠️ API yanıt vermedi, logları kontrol edin\n'));
324
+ }
325
+
326
+ function stopDaemon() {
327
+ if (!isDaemonRunning()) {
328
+ console.log(chalk.gray('\n⚠️ Daemon not running\n'));
329
+ return;
330
+ }
331
+
332
+ const pid = parseInt(fs.readFileSync(SIGNAL_PID_FILE, 'utf-8').trim());
333
+ try {
334
+ if (process.platform === 'win32') {
335
+ execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
336
+ } else {
337
+ process.kill(pid, 'SIGTERM');
338
+ setTimeout(() => {
339
+ try { process.kill(pid, 0); process.kill(pid, 'SIGKILL'); } catch {}
340
+ }, 3000);
341
+ }
342
+ if (fs.existsSync(SIGNAL_PID_FILE)) {
343
+ fs.unlinkSync(SIGNAL_PID_FILE);
344
+ }
345
+ console.log(chalk.green('\n✅ Daemon durduruldu\n'));
346
+ } catch (err) {
347
+ console.log(chalk.red(`\n❌ Hata: ${err.message}\n`));
348
+ }
349
+ }
350
+
351
+ function stopDaemonProcess() {
352
+ if (isDaemonRunning()) {
353
+ const pid = parseInt(fs.readFileSync(SIGNAL_PID_FILE, 'utf-8').trim());
354
+ try {
355
+ if (process.platform === 'win32') {
356
+ execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
357
+ } else {
358
+ process.kill(pid, 'SIGTERM');
359
+ }
360
+ } catch {}
361
+ if (fs.existsSync(SIGNAL_PID_FILE)) {
362
+ fs.unlinkSync(SIGNAL_PID_FILE);
363
+ }
364
+ }
365
+ }
366
+
367
+ function daemonStatus() {
368
+ const running = isDaemonRunning();
369
+ console.log(running
370
+ ? chalk.green(`\n✅ Daemon is running (PID: ${fs.readFileSync(SIGNAL_PID_FILE, 'utf-8').trim()})\n`)
371
+ : chalk.gray('\n⚠️ Daemon is not running\n'));
372
+ }
373
+
374
+ function isDaemonRunning() {
375
+ if (!fs.existsSync(SIGNAL_PID_FILE)) return false;
376
+ try {
377
+ const pid = parseInt(fs.readFileSync(SIGNAL_PID_FILE, 'utf-8').trim());
378
+ process.kill(pid, 0);
379
+ return true;
380
+ } catch {
381
+ if (fs.existsSync(SIGNAL_PID_FILE)) {
382
+ fs.unlinkSync(SIGNAL_PID_FILE);
383
+ }
384
+ return false;
385
+ }
386
+ }
387
+
388
+ async function probeSignalCli() {
389
+ const config = getConfig();
390
+ if (!config.signalHttpUrl) {
391
+ console.log(chalk.red('\n❌ Signal bağlantısı yapılmamış\n'));
392
+ console.log(chalk.gray('Önce: natureco signal connect\n'));
393
+ process.exit(1);
394
+ }
395
+
396
+ const baseUrl = config.signalHttpUrl;
397
+ const mode = config.signalApiMode || 'auto';
398
+ console.log(chalk.yellow(`\n⏳ Problanıyor: ${baseUrl}\n`));
399
+
400
+ let nativeOk = false;
401
+ let containerOk = false;
402
+
403
+ // Try native mode
404
+ try {
405
+ const res = await fetch(`${baseUrl}/api/v1/check`, { signal: AbortSignal.timeout(5000) });
406
+ nativeOk = res.ok;
407
+ if (nativeOk) {
408
+ const text = await res.text();
409
+ console.log(chalk.green('✓ Native JSON-RPC API:'), chalk.white(res.status, text.slice(0, 100)));
410
+ }
411
+ } catch (err) {
412
+ console.log(chalk.gray(`✗ Native JSON-RPC API: ${err.message}`));
413
+ }
414
+
415
+ // Try container mode
416
+ try {
417
+ const res = await fetch(`${baseUrl}/v1/about`, { signal: AbortSignal.timeout(5000) });
418
+ containerOk = res.ok;
419
+ if (containerOk) {
420
+ const data = await res.json();
421
+ console.log(chalk.green('✓ Container REST API:'), chalk.white(JSON.stringify(data).slice(0, 100)));
422
+ }
423
+ } catch (err) {
424
+ console.log(chalk.gray(`✗ Container REST API: ${err.message}`));
425
+ }
426
+
427
+ if (mode === 'auto') {
428
+ const detected = nativeOk ? 'native' : containerOk ? 'container' : 'none';
429
+ console.log(chalk.cyan(`\nAuto-detect sonucu: ${detected}`));
430
+ }
431
+
432
+ const cliPath = findSignalCli();
433
+ if (cliPath) {
434
+ console.log(chalk.gray(`\nsignal-cli binary: ${cliPath}`));
435
+ try {
436
+ const versionOut = execSync(`"${cliPath}" --version`, { encoding: 'utf-8', timeout: 10000 });
437
+ console.log(chalk.gray(`Version: ${versionOut.trim()}`));
438
+ } catch {
439
+ console.log(chalk.gray('Version sorgulanamadı'));
440
+ }
441
+ }
442
+
443
+ console.log('');
444
+ }
445
+
446
+ function probeSync(config) {
447
+ if (!config.signalHttpUrl) return false;
448
+ const baseUrl = config.signalHttpUrl;
449
+ try {
450
+ const res = execSync(
451
+ `powershell -Command "try { $r = Invoke-WebRequest -Uri '${baseUrl}/api/v1/check' -TimeoutSec 3 -UseBasicParsing; exit 0 } catch { exit 1 }"`,
452
+ { timeout: 10000, stdio: 'pipe' }
453
+ );
454
+ return true;
455
+ } catch {
456
+ // Also check container mode
457
+ try {
458
+ execSync(
459
+ `powershell -Command "try { $r = Invoke-WebRequest -Uri '${baseUrl}/v1/about' -TimeoutSec 3 -UseBasicParsing; exit 0 } catch { exit 1 }"`,
460
+ { timeout: 10000, stdio: 'pipe' }
461
+ );
462
+ return true;
463
+ } catch {
464
+ return false;
465
+ }
466
+ }
467
+ }
468
+
469
+ function findSignalCli() {
470
+ const config = getConfig();
471
+ if (config.signalCliPath && fs.existsSync(config.signalCliPath)) {
472
+ return config.signalCliPath;
473
+ }
474
+
475
+ // Check installed locations
476
+ const candidates = [
477
+ path.join(SIGNAL_DIR, `signal-cli-${SIGNAL_CLI_VERSION}`, 'bin', 'signal-cli' + (process.platform === 'win32' ? '.bat' : '')),
478
+ path.join(SIGNAL_DIR, `signal-cli-${SIGNAL_CLI_VERSION}`, 'bin', 'signal-cli'),
479
+ ];
480
+
481
+ for (const candidate of candidates) {
482
+ if (fs.existsSync(candidate)) return candidate;
483
+ }
484
+
485
+ // Check PATH
486
+ try {
487
+ const which = execSync(process.platform === 'win32' ? 'where signal-cli' : 'which signal-cli', { encoding: 'utf-8', timeout: 5000 });
488
+ const p = which.trim().split('\n')[0];
489
+ if (p && fs.existsSync(p)) return p;
490
+ } catch {}
491
+
492
+ return null;
493
+ }
494
+
495
+ module.exports = signal;
@@ -1,6 +1,7 @@
1
1
  const chalk = require('chalk');
2
2
  const inquirer = require('../utils/inquirer-wrapper');
3
3
  const { getSkills, installSkill, removeSkill, updateAllSkills, createSkillTemplate, getPopularSkills } = require('../utils/skills');
4
+ const { NatureCoError, SkillError, handleError } = require('../utils/errors');
4
5
 
5
6
  async function skills(args) {
6
7
  const [action, ...params] = args;
@@ -167,11 +168,15 @@ async function createSkillCommand(name) {
167
168
 
168
169
  async function searchSkillsCommand(query) {
169
170
  if (!query || query.trim().length === 0) {
170
- const popularSkills = await getPopularSkills();
171
- console.log(chalk.yellow('\nPopüler Skill\'ler:\n'));
172
- popularSkills.forEach(skill => {
173
- console.log(chalk.cyan(` ${skill.name.padEnd(15)}`), chalk.gray(skill.description));
174
- });
171
+ try {
172
+ const popularSkills = await getPopularSkills();
173
+ console.log(chalk.yellow('\nPopüler Skill\'ler:\n'));
174
+ popularSkills.forEach(skill => {
175
+ console.log(chalk.cyan(` ${skill.name.padEnd(15)}`), chalk.gray(skill.description));
176
+ });
177
+ } catch (err) {
178
+ console.log(chalk.red(`\n ❌ Popüler skill'ler alınamadı: ${err.message}\n`));
179
+ }
175
180
  console.log('');
176
181
  console.log(chalk.gray('Kurmak için: '), chalk.cyan('natureco skills install <slug>'));
177
182
  console.log(chalk.gray('Örnek: '), chalk.cyan('natureco skills install github'));
@@ -188,14 +193,14 @@ async function searchSkillsCommand(query) {
188
193
  const response = await fetch(searchUrl);
189
194
 
190
195
  if (!response.ok) {
191
- throw new Error('ClawHub API\'ye erişilemedi');
196
+ throw new SkillError('ClawHub API\'ye erişilemedi', 'fetch', `https://clawhub.ai/api/skills?q=${encodeURIComponent(query)}`);
192
197
  }
193
198
 
194
199
  let data;
195
200
  try {
196
201
  data = await response.json();
197
202
  } catch {
198
- throw new Error('ClawHub geçersiz yanıt döndü (JSON bekleniyordu)');
203
+ throw new SkillError('ClawHub geçersiz yanıt döndü (JSON bekleniyordu)', 'parse', searchUrl);
199
204
  }
200
205
  const results = data.skills || [];
201
206
 
@@ -211,32 +216,18 @@ async function searchSkillsCommand(query) {
211
216
  });
212
217
  console.log('');
213
218
  } catch (err) {
214
- // Fallback to local popular skills search
215
- console.log(chalk.gray('ClawHub\'a bağlanılamadı, yerel aramada...\n'));
216
-
217
- const popularSkills = await getPopularSkills();
218
- const results = popularSkills.filter(skill =>
219
- skill.name.toLowerCase().includes(query.toLowerCase()) ||
220
- skill.description.toLowerCase().includes(query.toLowerCase()) ||
221
- skill.slug.toLowerCase().includes(query.toLowerCase())
222
- );
223
-
224
- if (results.length === 0) {
225
- console.log(chalk.yellow(`"${query}" için sonuç bulunamadı.\n`));
226
- return;
227
- }
228
-
229
- console.log(chalk.yellow(`"${query}" için ${results.length} sonuç:\n`));
230
- results.forEach(skill => {
231
- console.log(chalk.cyan(` ${skill.name.padEnd(15)}`), chalk.gray(skill.description));
232
- console.log(chalk.gray(` Kurmak için: natureco skills install ${skill.slug}`));
233
- });
234
- console.log('');
219
+ console.log(chalk.red(`\n ❌ Arama başarısız: ${err.message}\n`));
235
220
  }
236
221
  }
237
222
 
238
223
  async function browseSkillsCommand() {
239
- const popularSkills = await getPopularSkills();
224
+ let popularSkills;
225
+ try {
226
+ popularSkills = await getPopularSkills();
227
+ } catch (err) {
228
+ console.log(chalk.red(`\n ❌ Popüler skill'ler alınamadı: ${err.message}\n`));
229
+ return;
230
+ }
240
231
 
241
232
  console.log(chalk.green.bold('\n╭─ Popüler Skill\'ler ─╮\n'));
242
233