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,46 +1,178 @@
1
1
  const chalk = require('chalk');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const os = require('os');
2
5
  const { getConfig } = require('../utils/config');
3
6
 
7
+ const PEERS_FILE = path.join(os.homedir(), '.natureco', 'peers.json');
8
+
9
+ function loadPeers() {
10
+ try {
11
+ return JSON.parse(fs.readFileSync(PEERS_FILE, 'utf8'));
12
+ } catch {
13
+ return [];
14
+ }
15
+ }
16
+
17
+ function savePeers(peers) {
18
+ const dir = path.dirname(PEERS_FILE);
19
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
20
+ fs.writeFileSync(PEERS_FILE, JSON.stringify(peers, null, 2));
21
+ }
22
+
4
23
  function directory(args) {
5
24
  const [action, ...params] = args || [];
6
25
 
7
26
  if (!action || action === 'self') return showSelf();
8
- if (action === 'channels' || action === 'peers') return listChannels();
27
+ if (action === 'channels' || action === 'peers') return listPeers();
28
+ if (action === 'search') return searchPeers(params.join(' '));
29
+ if (action === 'register') return registerPeer(params[0], params.slice(1).join(' '));
30
+ if (action === 'remove') return removePeer(params[0]);
31
+ if (action === 'groups' && params[0] === 'list') return groupsList();
32
+ if (action === 'groups' && params[0] === 'members') return groupsMembers(params[1]);
33
+ if (action === 'groups') return groupsList();
9
34
 
10
35
  console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
11
- console.log(chalk.gray(' Kullanım: natureco directory [self|channels]\n'));
36
+ console.log(chalk.gray(' Kullanım: natureco directory [self|peers|search|register|remove|groups]\n'));
12
37
  process.exit(1);
13
38
  }
14
39
 
15
40
  function showSelf() {
16
41
  const config = getConfig();
17
- console.log(chalk.cyan('\n 👤 Self\n'));
42
+ console.log(chalk.cyan('\n Self\n'));
18
43
  console.log(chalk.gray(' ' + '─'.repeat(48)));
19
44
  console.log(` ${chalk.white('Name:')} ${chalk.cyan(config.agentName || process.env.USER || 'user')}`);
20
45
  console.log(` ${chalk.white('Gateway:')} ${chalk.gray(config.gatewayUrl || 'not configured')}`);
21
46
  console.log(` ${chalk.white('Provider:')} ${chalk.gray(config.provider || 'not set')}`);
22
47
  console.log(` ${chalk.white('Model:')} ${chalk.gray(config.model || 'not set')}`);
23
48
  console.log(` ${chalk.white('User ID:')} ${chalk.gray(config.userId || 'local')}`);
49
+ console.log(` ${chalk.white('Peers:')} ${chalk.gray(`${loadPeers().length} registered`)}`);
50
+ console.log(` ${chalk.white('Host:')} ${chalk.gray(os.hostname())}`);
24
51
  console.log();
25
52
  }
26
53
 
27
- function listChannels() {
54
+ function listPeers() {
28
55
  const config = getConfig();
29
- console.log(chalk.cyan('\n 📡 Channels\n'));
30
- console.log(chalk.gray(' ' + '─'.repeat(48)));
56
+ const peers = loadPeers();
57
+
58
+ if (peers.length === 0) {
59
+ console.log(chalk.cyan('\n Directory\n'));
60
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
31
61
 
32
- const channels = ['telegram', 'whatsapp', 'discord', 'slack', 'signal', 'irc', 'mattermost', 'imessage', 'sms'];
33
- let found = false;
62
+ const channels = ['telegram', 'whatsapp', 'discord', 'slack', 'signal', 'irc', 'mattermost', 'imessage', 'sms'];
63
+ let found = false;
34
64
 
35
- for (const ch of channels) {
36
- if (config[ch]) {
37
- const info = typeof config[ch] === 'object' ? Object.keys(config[ch]).join(', ') : 'connected';
38
- console.log(` ${chalk.green('●')} ${chalk.white(ch)}: ${chalk.gray(info)}`);
39
- found = true;
65
+ for (const ch of channels) {
66
+ if (config[ch]) {
67
+ const info = typeof config[ch] === 'object' ? Object.keys(config[ch]).join(', ') : 'connected';
68
+ console.log(` ${chalk.green('●')} ${chalk.white(ch)}: ${chalk.gray(info)}`);
69
+ found = true;
70
+ }
40
71
  }
72
+
73
+ if (!found) console.log(chalk.gray(' No channels configured.'));
74
+ console.log(chalk.gray('\n Register peers: ') + chalk.cyan('natureco directory register <url> [name]'));
75
+ console.log();
76
+ return;
77
+ }
78
+
79
+ console.log(chalk.cyan('\n Known Peers\n'));
80
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
81
+ peers.forEach(p => {
82
+ const status = p.lastSeen ? chalk.gray(`(seen: ${new Date(p.lastSeen).toLocaleDateString()})`) : '';
83
+ console.log(` ${chalk.green('●')} ${chalk.white(p.name || p.url)}`);
84
+ console.log(` ${chalk.gray(p.url)} ${status}`);
85
+ });
86
+ console.log(chalk.gray(`\n Total: ${peers.length} peers`));
87
+ console.log();
88
+ }
89
+
90
+ function searchPeers(query) {
91
+ if (!query) {
92
+ console.log(chalk.red('\n ❌ Arama sorgusu gerekli\n'));
93
+ console.log(chalk.gray(' Kullanım: natureco directory search <query>\n'));
94
+ process.exit(1);
95
+ }
96
+
97
+ const peers = loadPeers();
98
+ const lower = query.toLowerCase();
99
+ const results = peers.filter(p =>
100
+ (p.name && p.name.toLowerCase().includes(lower)) ||
101
+ (p.url && p.url.toLowerCase().includes(lower)) ||
102
+ (p.tags && p.tags.some(t => t.toLowerCase().includes(lower)))
103
+ );
104
+
105
+ if (results.length === 0) {
106
+ console.log(chalk.yellow(`\n "${query}" için eşleşen peer bulunamadı.\n`));
107
+ return;
108
+ }
109
+
110
+ console.log(chalk.cyan(`\n Search Results (${results.length}) for: "${query}"\n`));
111
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
112
+ results.forEach(p => {
113
+ console.log(` ${chalk.green('●')} ${chalk.white(p.name || p.url)}`);
114
+ console.log(` ${chalk.gray(p.url)}`);
115
+ if (p.tags) console.log(` ${chalk.gray('tags: ' + p.tags.join(', '))}`);
116
+ });
117
+ console.log();
118
+ }
119
+
120
+ function registerPeer(url, name) {
121
+ if (!url) {
122
+ console.log(chalk.red('\n ❌ URL gerekli\n'));
123
+ console.log(chalk.gray(' Kullanım: natureco directory register <url> [name]\n'));
124
+ process.exit(1);
125
+ }
126
+
127
+ const peers = loadPeers();
128
+ if (peers.some(p => p.url === url)) {
129
+ console.log(chalk.yellow(`\n ⚠ Bu peer zaten kayıtlı: ${url}\n`));
130
+ return;
131
+ }
132
+
133
+ peers.push({ url, name: name || url, tags: [], addedAt: new Date().toISOString(), lastSeen: null });
134
+ savePeers(peers);
135
+ console.log(chalk.green(`\n ✓ Peer kaydedildi: ${name || url}\n`));
136
+ }
137
+
138
+ function removePeer(urlOrName) {
139
+ if (!urlOrName) {
140
+ console.log(chalk.red('\n ❌ URL veya isim gerekli\n'));
141
+ console.log(chalk.gray(' Kullanım: natureco directory remove <url|name>\n'));
142
+ process.exit(1);
41
143
  }
42
144
 
43
- if (!found) console.log(chalk.gray(' No channels configured.\n'));
145
+ let peers = loadPeers();
146
+ const initialLength = peers.length;
147
+ peers = peers.filter(p => p.url !== urlOrName && p.name !== urlOrName);
148
+
149
+ if (peers.length === initialLength) {
150
+ console.log(chalk.yellow(`\n ⚠ Peer bulunamadı: ${urlOrName}\n`));
151
+ return;
152
+ }
153
+
154
+ savePeers(peers);
155
+ console.log(chalk.green(`\n ✓ Peer kaldırıldı: ${urlOrName}\n`));
156
+ }
157
+
158
+ function groupsList() {
159
+ console.log(chalk.cyan('\n Groups: (stub)\n'));
160
+ }
161
+
162
+ function groupsMembers(name) {
163
+ if (!name) {
164
+ console.log(chalk.red('\n ❌ Group name gerekli\n'));
165
+ process.exit(1);
166
+ }
167
+ console.log(chalk.cyan('\n Members of group "' + name + '":\n'));
168
+ const peers = loadPeers().filter(p => p.tags && p.tags.includes('group:' + name));
169
+ if (peers.length === 0) {
170
+ console.log(chalk.gray(' No members found.\n'));
171
+ return;
172
+ }
173
+ for (const p of peers) {
174
+ console.log(` ${chalk.green('●')} ${chalk.white(p.name || p.url)}`);
175
+ }
44
176
  console.log();
45
177
  }
46
178
 
@@ -7,14 +7,15 @@ function dnsCmd(args) {
7
7
 
8
8
  if (!action || action === 'discover') return discoverNetwork();
9
9
  if (action === 'resolve') return resolveHost(params[0]);
10
+ if (action === 'services') return discoverServices();
10
11
 
11
12
  console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
12
- console.log(chalk.gray(' Kullanım: natureco dns [discover|resolve]\n'));
13
+ console.log(chalk.gray(' Kullanım: natureco dns [discover|resolve <host>|services]\n'));
13
14
  process.exit(1);
14
15
  }
15
16
 
16
17
  function discoverNetwork() {
17
- console.log(chalk.cyan('\n 🌐 Network Discovery (DNS)\n'));
18
+ console.log(chalk.cyan('\n Network Discovery (DNS)\n'));
18
19
  console.log(chalk.gray(' ' + '─'.repeat(48)));
19
20
 
20
21
  const interfaces = os.networkInterfaces();
@@ -30,29 +31,166 @@ function discoverNetwork() {
30
31
  }
31
32
  }
32
33
 
33
- console.log(chalk.gray('\n Service discovery for wide-area (Tailscale + CoreDNS).'));
34
- console.log(chalk.gray(' For full mDNS: ') + chalk.cyan('natureco bonjour scan\n'));
34
+ const tailscaleIP = detectTailscale();
35
+ if (tailscaleIP) {
36
+ console.log(`\n ${chalk.green('●')} ${chalk.white('Tailscale')}: ${chalk.cyan(tailscaleIP)}`);
37
+ const hostname = os.hostname();
38
+ dns.resolveTxt(`_natureco._tcp.${hostname}.tailscale.net`, (err, records) => {
39
+ if (!err && records) {
40
+ console.log(` ${chalk.gray(' Service:')} ${chalk.cyan(records.flat().join(', '))}`);
41
+ }
42
+ });
43
+ }
44
+
45
+ const zerotier = detectZeroTier();
46
+ if (zerotier) {
47
+ console.log(` ${chalk.green('●')} ${chalk.white('ZeroTier')}: ${chalk.cyan(zerotier)}`);
48
+ }
49
+
50
+ console.log(chalk.gray('\n mDNS: ') + chalk.cyan('natureco bonjour scan'));
51
+ console.log(chalk.gray(' Resolve: ') + chalk.cyan('natureco dns resolve <hostname>'));
52
+ console.log(chalk.gray(' Services: ') + chalk.cyan('natureco dns services\n'));
53
+ }
54
+
55
+ function detectTailscale() {
56
+ try {
57
+ const tailscaleDir = os.platform() === 'win32'
58
+ ? os.homedir() + '\\AppData\\Local\\Tailscale'
59
+ : '/var/run/tailscale';
60
+ if (!require('fs').existsSync(tailscaleDir)) return null;
61
+
62
+ const interfaces = os.networkInterfaces();
63
+ for (const [, addrs] of Object.entries(interfaces)) {
64
+ if (!addrs) continue;
65
+ for (const addr of addrs) {
66
+ if (addr.family === 'IPv4' && addr.address.startsWith('100.')) {
67
+ return addr.address;
68
+ }
69
+ }
70
+ }
71
+ } catch {}
72
+ return null;
73
+ }
74
+
75
+ function detectZeroTier() {
76
+ try {
77
+ const interfaces = os.networkInterfaces();
78
+ for (const [, addrs] of Object.entries(interfaces)) {
79
+ if (!addrs) continue;
80
+ for (const addr of addrs) {
81
+ if (addr.family === 'IPv4' && (
82
+ addr.address.startsWith('10.144.') ||
83
+ addr.address.startsWith('10.147.')
84
+ )) {
85
+ return addr.address;
86
+ }
87
+ }
88
+ }
89
+ } catch {}
90
+ return null;
35
91
  }
36
92
 
37
93
  function resolveHost(hostname) {
38
94
  if (!hostname) {
39
95
  console.log(chalk.red('\n ❌ Hostname gerekli\n'));
96
+ console.log(chalk.gray(' Kullanım: natureco dns resolve <hostname>\n'));
40
97
  process.exit(1);
41
98
  }
42
99
 
43
- console.log(chalk.cyan(`\n 🔍 Resolving: ${hostname}\n`));
100
+ console.log(chalk.cyan(`\n Resolving: ${hostname}\n`));
44
101
 
45
102
  dns.resolve4(hostname, (err, addresses) => {
46
- if (err) {
103
+ if (err && err.code !== 'ENODATA') {
47
104
  console.log(chalk.red(` ❌ ${err.message}\n`));
48
105
  return;
49
106
  }
50
- console.log(chalk.gray(' ' + '─'.repeat(48)));
51
- for (const addr of addresses) {
52
- console.log(` ${chalk.white(hostname)} → ${chalk.cyan(addr)}`);
107
+
108
+ const results = [];
109
+ if (addresses && addresses.length > 0) {
110
+ for (const addr of addresses) {
111
+ results.push({ type: 'A', value: addr });
112
+ }
53
113
  }
54
- console.log();
114
+
115
+ dns.resolve6(hostname, (err6, addrs6) => {
116
+ if (!err6 && addrs6) {
117
+ for (const addr of addrs6) {
118
+ results.push({ type: 'AAAA', value: addr });
119
+ }
120
+ }
121
+
122
+ dns.resolveTxt(hostname, (errTxt, txts) => {
123
+ if (!errTxt && txts) {
124
+ for (const txt of txts) {
125
+ results.push({ type: 'TXT', value: txt.join(' ') });
126
+ }
127
+ }
128
+
129
+ dns.resolveSrv(`_natureco._tcp.${hostname}`, (errSrv, srvs) => {
130
+ if (!errSrv && srvs) {
131
+ for (const srv of srvs) {
132
+ results.push({ type: 'SRV', value: `${srv.name}:${srv.port} (priority ${srv.priority}, weight ${srv.weight})` });
133
+ }
134
+ }
135
+
136
+ if (results.length === 0) {
137
+ console.log(chalk.gray(' No DNS records found.\n'));
138
+ return;
139
+ }
140
+
141
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
142
+ for (const r of results) {
143
+ console.log(` ${chalk.white(hostname)} ${chalk.cyan(r.type)} ${chalk.gray('→')} ${chalk.white(r.value)}`);
144
+ }
145
+ console.log();
146
+ });
147
+ });
148
+ });
55
149
  });
56
150
  }
57
151
 
152
+ function discoverServices() {
153
+ console.log(chalk.cyan('\n Service Discovery (DNS-SD)\n'));
154
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
155
+
156
+ const services = [
157
+ '_natureco._tcp',
158
+ '_natureco-gateway._tcp',
159
+ '_natureco-dashboard._tcp',
160
+ '_mcp._tcp',
161
+ '_openclaw._tcp',
162
+ ];
163
+
164
+ let pending = services.length;
165
+ let found = false;
166
+
167
+ for (const svc of services) {
168
+ dns.resolveSrv(svc + '.local', { hints: dns.ADDRCONFIG }, (err, addresses) => {
169
+ if (!err && addresses && addresses.length > 0) {
170
+ found = true;
171
+ console.log(` ${chalk.green('●')} ${chalk.white(svc)}`);
172
+ for (const addr of addresses) {
173
+ dns.resolve4(addr.name, { hints: dns.ADDRCONFIG }, (err4, ips) => {
174
+ const ip = ips && ips.length > 0 ? `(${ips[0]})` : '';
175
+ console.log(` ${chalk.gray(`${addr.name}:${addr.port} ${ip}`)}`);
176
+ });
177
+ }
178
+ console.log();
179
+ }
180
+ pending--;
181
+ if (pending === 0 && !found) {
182
+ console.log(chalk.gray(' No NatureCo services discovered via DNS-SD.\n'));
183
+ console.log(chalk.gray(' Try: ') + chalk.cyan('natureco bonjour discover'));
184
+ console.log(chalk.gray(' Try: ') + chalk.cyan('natureco dns discover\n'));
185
+ }
186
+ });
187
+ }
188
+
189
+ setTimeout(() => {
190
+ if (!found) {
191
+ console.log(chalk.gray(' Discovering... (may take a moment)\n'));
192
+ }
193
+ }, 500);
194
+ }
195
+
58
196
  module.exports = dnsCmd;
@@ -1,43 +1,136 @@
1
1
  const chalk = require('chalk');
2
2
 
3
+ const DOC_SOURCES = [
4
+ { name: 'NatureCo API', url: 'https://api.natureco.me/api/v1/docs/search?q=' },
5
+ { name: 'NatureCo CLI', url: 'https://natureco.me/cli/' },
6
+ { name: 'NatureCo Developers', url: 'https://developers.natureco.me/' },
7
+ { name: 'OpenClaw Docs', url: 'https://docs.openclaw.ai/' },
8
+ ];
9
+
3
10
  async function docs(args) {
4
11
  const query = args.join(' ').trim();
5
12
 
6
13
  if (!query) {
7
- console.log(chalk.cyan('\n 📚 NatureCo Docs Search\n'));
8
- console.log(chalk.gray(' ' + '─'.repeat(48)));
9
- console.log(chalk.gray(' Search live documentation from the web.\n'));
10
- console.log(chalk.gray(' Usage:'));
11
- console.log(chalk.cyan(' natureco docs <search query>'));
12
- console.log(chalk.gray('\n Examples:'));
13
- console.log(chalk.gray(' natureco docs how to setup gateway'));
14
- console.log(chalk.gray(' natureco docs channels telegram'));
15
- console.log();
14
+ showHelp();
16
15
  return;
17
16
  }
18
17
 
19
- console.log(chalk.cyan(`\n 🔍 Searching docs for: "${query}"\n`));
18
+ console.log(chalk.cyan(`\n Searching docs for: "${query}"\n`));
19
+
20
+ let found = false;
21
+
22
+ for (const source of DOC_SOURCES) {
23
+ try {
24
+ const url = source.url.includes('/docs/search')
25
+ ? source.url + encodeURIComponent(query)
26
+ : source.url;
27
+ const response = await fetch(url + (source.url.includes('/docs/search') ? '' : '?format=json'), {
28
+ headers: { 'User-Agent': 'NatureCo-CLI/2.0' },
29
+ signal: AbortSignal.timeout(3000),
30
+ });
31
+
32
+ if (response.ok) {
33
+ const text = await response.text();
34
+ let data;
35
+ try { data = JSON.parse(text); } catch { data = null; }
36
+
37
+ if (data && data.results && data.results.length > 0) {
38
+ found = true;
39
+ console.log(chalk.cyan(` ${source.name}\n`));
40
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
41
+ for (const r of data.results.slice(0, 5)) {
42
+ console.log(` ${chalk.white(r.title || r.name || 'Result')}`);
43
+ if (r.description) console.log(` ${chalk.gray(r.description)}`);
44
+ if (r.url) console.log(` ${chalk.cyan(r.url)}`);
45
+ console.log();
46
+ }
47
+ } else if (data && data.AbstractText) {
48
+ found = true;
49
+ console.log(chalk.cyan(` ${source.name}\n`));
50
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
51
+ console.log(` ${chalk.white(data.AbstractText)}`);
52
+ if (data.AbstractURL) console.log(`\n ${chalk.cyan(data.AbstractURL)}`);
53
+ console.log();
54
+ }
55
+ }
56
+ } catch {}
57
+ }
58
+
59
+ if (!found) {
60
+ try {
61
+ const webRes = await fetch(`https://api.duckduckgo.com/?q=${encodeURIComponent('site:natureco.me ' + query)}&format=json&no_html=1&skip_disambig=1`, {
62
+ headers: { 'User-Agent': 'NatureCo-CLI/2.0' },
63
+ signal: AbortSignal.timeout(5000),
64
+ });
20
65
 
21
- try {
22
- const response = await fetch(`https://api.duckduckgo.com/?q=${encodeURIComponent('natureco ' + query)}&format=json&no_html=1`, {
23
- headers: { 'User-Agent': 'NatureCo-CLI/2.0' }
24
- });
66
+ if (webRes.ok) {
67
+ const webData = await webRes.json();
68
+ if (webData.AbstractText || webData.RelatedTopics?.length > 0) {
69
+ found = true;
70
+ console.log(chalk.cyan(' NatureCo (via DuckDuckGo)\n'));
71
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
72
+ if (webData.AbstractText) {
73
+ console.log(` ${chalk.white(webData.AbstractText)}`);
74
+ if (webData.AbstractURL) console.log(`\n ${chalk.cyan(webData.AbstractURL)}`);
75
+ } else {
76
+ for (const topic of webData.RelatedTopics.slice(0, 5)) {
77
+ if (topic.Text) {
78
+ console.log(` ${chalk.white(topic.Text)}`);
79
+ if (topic.FirstURL) console.log(` ${chalk.cyan(topic.FirstURL)}`);
80
+ console.log();
81
+ }
82
+ }
83
+ }
84
+ console.log();
85
+ }
86
+ }
87
+ } catch {}
88
+ }
25
89
 
26
- if (!response.ok) throw new Error(`Search error: ${response.status}`);
90
+ if (!found) {
91
+ try {
92
+ const htmlRes = await fetch('https://natureco.me/cli/', {
93
+ headers: { 'User-Agent': 'NatureCo-CLI/2.0' },
94
+ signal: AbortSignal.timeout(3000),
95
+ });
96
+ if (htmlRes.ok) {
97
+ const html = await htmlRes.text();
98
+ const lines = html.split('\n').filter(l => l.toLowerCase().includes(query.toLowerCase()));
99
+ if (lines.length > 0) {
100
+ found = true;
101
+ console.log(chalk.cyan(' NatureCo CLI Docs Page\n'));
102
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
103
+ for (const line of lines.slice(0, 5)) {
104
+ const clean = line.replace(/<[^>]*>/g, '').trim();
105
+ if (clean) console.log(` ${chalk.white(clean.substring(0, 120))}`);
106
+ }
107
+ console.log(`\n ${chalk.cyan('https://natureco.me/cli/')}`);
108
+ console.log();
109
+ }
110
+ }
111
+ } catch {}
112
+ }
27
113
 
28
- const data = await response.json();
29
- if (data.AbstractText) {
30
- console.log(chalk.white(data.AbstractText));
31
- if (data.AbstractURL) console.log(chalk.gray(`\n Source: ${data.AbstractURL}`));
32
- } else {
33
- console.log(chalk.gray(' No documentation found for this query.'));
34
- console.log(chalk.gray('\n Try searching the web:'));
35
- console.log(chalk.cyan(` natureco web_search query="${query}"`));
36
- }
114
+ if (!found) {
115
+ console.log(chalk.gray(' No documentation found for this query.'));
116
+ console.log(chalk.gray('\n Try browsing:'));
117
+ console.log(chalk.cyan(' https://natureco.me/cli/'));
118
+ console.log(chalk.cyan(' https://developers.natureco.me/'));
37
119
  console.log();
38
- } catch (err) {
39
- console.log(chalk.red(` ❌ ${err.message}\n`));
40
120
  }
41
121
  }
42
122
 
123
+ function showHelp() {
124
+ console.log(chalk.cyan('\n NatureCo Docs Search\n'));
125
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
126
+ console.log(chalk.gray(' Search NatureCo documentation from multiple sources.\n'));
127
+ console.log(chalk.gray(' Usage:'));
128
+ console.log(chalk.cyan(' natureco docs <search query>'));
129
+ console.log(chalk.gray('\n Examples:'));
130
+ console.log(chalk.gray(' natureco docs gateway setup'));
131
+ console.log(chalk.gray(' natureco docs telegram channel'));
132
+ console.log(chalk.gray(' natureco docs api reference'));
133
+ console.log();
134
+ }
135
+
43
136
  module.exports = docs;