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,267 +1,422 @@
1
- const chalk = require('chalk');
2
- const { getConfig, saveConfig } = require('../utils/config');
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
-
7
- async function channels(args) {
8
- const [action, ...params] = (args || []);
9
-
10
- if (!action || action === 'list') return listChannels();
11
- if (action === 'status') return statusChannels();
12
- if (action === 'remove') return removeChannel(params[0]);
13
- if (action === 'logs') return channelLogs(params[0]);
14
-
15
- console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
16
- console.log(chalk.gray(' Kullanım: natureco channels [list|status|remove|logs]\n'));
17
- process.exit(1);
18
- }
19
-
20
- function listChannels() {
21
- const config = getConfig();
22
- const channels = [];
23
-
24
- if (config.telegramToken) {
25
- channels.push({
26
- name: 'Telegram',
27
- icon: '✈',
28
- status: 'bağlı',
29
- detail: `token: ${config.telegramToken.slice(0, 12)}...`,
30
- chats: (config.telegramAllowedChats || []).length,
31
- });
32
- }
33
-
34
- if (config.whatsappConnected) {
35
- channels.push({
36
- name: 'WhatsApp',
37
- icon: '📱',
38
- status: 'bağlı',
39
- detail: config.whatsappPhone ? `+${config.whatsappPhone.split(':')[0]}` : 'bağlı',
40
- chats: (config.whatsappAllowedNumbers || []).length,
41
- });
42
- }
43
-
44
- if (config.discordToken) {
45
- channels.push({
46
- name: 'Discord',
47
- icon: '🎮',
48
- status: 'bağlı',
49
- detail: `token: ${config.discordToken.slice(0, 12)}...`,
50
- chats: 0,
51
- });
52
- }
53
-
54
- if (config.slackToken) {
55
- channels.push({
56
- name: 'Slack',
57
- icon: '💼',
58
- status: 'bağlı',
59
- detail: `token: ${config.slackToken.slice(0, 12)}...`,
60
- chats: 0,
61
- });
62
- }
63
-
64
- if (config.signalBotId) {
65
- channels.push({
66
- name: 'Signal',
67
- icon: '🔒',
68
- status: 'bağlı',
69
- detail: `hesap: ${config.signalAccount || 'bağlı'}`,
70
- chats: 0,
71
- });
72
- }
73
-
74
- if (config.ircBotId) {
75
- channels.push({
76
- name: 'IRC',
77
- icon: '💬',
78
- status: 'bağlı',
79
- detail: `${config.ircHost}:${config.ircPort} (${config.ircNick})`,
80
- chats: 0,
81
- });
82
- }
83
-
84
- if (config.mattermostBotId) {
85
- channels.push({
86
- name: 'Mattermost',
87
- icon: '📢',
88
- status: 'bağlı',
89
- detail: `sunucu: ${config.mattermostBaseUrl || 'bağlı'}`,
90
- chats: 0,
91
- });
92
- }
93
-
94
- if (config.imessageBotId) {
95
- channels.push({
96
- name: 'iMessage',
97
- icon: '💬',
98
- status: 'bağlı',
99
- detail: 'macOS bridge',
100
- chats: 0,
101
- });
102
- }
103
-
104
- if (config.smsBotId) {
105
- channels.push({
106
- name: 'SMS (Twilio)',
107
- icon: '📨',
108
- status: 'bağlı',
109
- detail: `numara: ${config.smsFromNumber || 'bağlı'}`,
110
- chats: 0,
111
- });
112
- }
113
-
114
- if (config.webhooks && config.webhooks.length > 0) {
115
- channels.push({
116
- name: 'Webhooks',
117
- icon: '🔗',
118
- status: 'bağlı',
119
- detail: `${config.webhooks.length} route`,
120
- chats: 0,
121
- });
122
- }
123
-
124
- console.log('');
125
- console.log(chalk.gray(' ' + ''.repeat(48)));
126
- console.log(chalk.cyan.bold('\n Kanallar\n'));
127
-
128
- if (channels.length === 0) {
129
- console.log(chalk.gray(' Bağlı kanal yok.\n'));
130
- console.log(chalk.gray(' Bağlamak için:'));
131
- console.log(chalk.cyan(' natureco telegram connect'));
132
- console.log(chalk.cyan(' natureco whatsapp connect'));
133
- console.log(chalk.cyan(' natureco discord connect'));
134
- console.log(chalk.cyan(' natureco slack connect'));
135
- console.log(chalk.cyan(' natureco signal connect'));
136
- console.log(chalk.cyan(' natureco irc connect'));
137
- console.log(chalk.cyan(' natureco mattermost connect'));
138
- console.log(chalk.cyan(' natureco imessage connect'));
139
- console.log(chalk.cyan(' natureco sms connect'));
140
- console.log(chalk.cyan(' natureco webhooks connect\n'));
141
- return;
142
- }
143
-
144
- channels.forEach(ch => {
145
- console.log(chalk.white(` ${ch.icon} ${ch.name}`) + chalk.green(' ✓ ' + ch.status));
146
- console.log(chalk.gray(` ${ch.detail}`));
147
- if (ch.chats > 0) console.log(chalk.gray(` İzin listesi: ${ch.chats} kayıt`));
148
- console.log('');
149
- });
150
-
151
- console.log(chalk.gray(' ' + ''.repeat(48)));
152
- console.log(chalk.gray(` Toplam: ${channels.length} kanal bağlı`));
153
- console.log(chalk.gray(' Kaldırmak için: ') + chalk.cyan('natureco channels remove <kanal>\n'));
154
- }
155
-
156
- function statusChannels() {
157
- const config = getConfig();
158
- console.log('');
159
- console.log(chalk.gray(' ' + '─'.repeat(48)));
160
- console.log(chalk.cyan.bold('\n Kanal Durumu\n'));
161
-
162
- const checks = [
163
- { name: 'Telegram', ok: !!config.telegramToken },
164
- { name: 'WhatsApp', ok: !!config.whatsappConnected },
165
- { name: 'Discord', ok: !!config.discordToken },
166
- { name: 'Slack', ok: !!config.slackToken },
167
- { name: 'Signal', ok: !!config.signalBotId },
168
- { name: 'IRC', ok: !!config.ircBotId },
169
- { name: 'Mattermost', ok: !!config.mattermostBotId },
170
- { name: 'iMessage', ok: !!config.imessageBotId },
171
- { name: 'SMS', ok: !!config.smsBotId },
172
- { name: 'Webhooks', ok: !!(config.webhooks && config.webhooks.length > 0) },
173
- ];
174
-
175
- checks.forEach(ch => {
176
- const icon = ch.ok ? chalk.green('✓') : chalk.gray('○');
177
- const label = ch.ok ? chalk.white(ch.name) : chalk.gray(ch.name);
178
- const status = ch.ok ? chalk.green('bağlı') : chalk.gray('bağlı değil');
179
- console.log(` ${icon} ${label.padEnd(12)} ${status}`);
180
- });
181
-
182
- const connected = checks.filter(c => c.ok).length;
183
- console.log('');
184
- console.log(chalk.gray(' ' + '─'.repeat(48)));
185
- console.log(chalk.gray(` ${connected}/${checks.length} kanal aktif\n`));
186
- }
187
-
188
- function removeChannel(channel) {
189
- if (!channel) {
190
- console.log(chalk.red('\n ❌ Kanal adı gerekli\n'));
191
- console.log(chalk.gray(' Kullanım: natureco channels remove <telegram|whatsapp|discord|slack|signal|irc|mattermost|imessage|sms|webhooks>\n'));
192
- process.exit(1);
193
- }
194
-
195
- const config = getConfig();
196
- const ch = channel.toLowerCase();
197
-
198
- if (ch === 'telegram') {
199
- delete config.telegramToken;
200
- delete config.telegramBotId;
201
- delete config.telegramAllowedChats;
202
- } else if (ch === 'whatsapp') {
203
- if (config.whatsappBotId) {
204
- const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
205
- if (fs.existsSync(sessionDir)) {
206
- fs.rmSync(sessionDir, { recursive: true, force: true });
207
- }
208
- }
209
- delete config.whatsappConnected;
210
- delete config.whatsappBotId;
211
- delete config.whatsappPhone;
212
- delete config.whatsappAllowedNumbers;
213
- } else if (ch === 'discord') {
214
- delete config.discordToken;
215
- delete config.discordBotId;
216
- } else if (ch === 'slack') {
217
- delete config.slackToken;
218
- delete config.slackBotId;
219
- } else if (ch === 'signal') {
220
- delete config.signalHttpUrl;
221
- delete config.signalAccount;
222
- delete config.signalDmPolicy;
223
- delete config.signalBotId;
224
- } else if (ch === 'irc') {
225
- delete config.ircHost; delete config.ircPort; delete config.ircTls;
226
- delete config.ircNick; delete config.ircUsername; delete config.ircPassword;
227
- delete config.ircChannels; delete config.ircDmPolicy; delete config.ircBotId;
228
- } else if (ch === 'mattermost') {
229
- delete config.mattermostBaseUrl; delete config.mattermostToken;
230
- delete config.mattermostDmPolicy; delete config.mattermostBotId;
231
- } else if (ch === 'imessage') {
232
- delete config.imessageCliPath; delete config.imessageDmPolicy; delete config.imessageBotId;
233
- } else if (ch === 'sms') {
234
- delete config.smsAccountSid; delete config.smsAuthToken; delete config.smsFromNumber;
235
- delete config.smsPublicWebhookUrl; delete config.smsDmPolicy; delete config.smsBotId;
236
- } else if (ch === 'webhooks') {
237
- delete config.webhooks; delete config.webhookEnabled;
238
- } else {
239
- console.log(chalk.red(`\n ❌ Bilinmeyen kanal: ${channel}\n`));
240
- process.exit(1);
241
- }
242
-
243
- saveConfig(config);
244
- console.log(chalk.green(`\n ✓ ${channel} kaldırıldı\n`));
245
- }
246
-
247
- function channelLogs(channel) {
248
- const logFile = path.join(os.homedir(), '.natureco', 'gateway.log');
249
- if (!fs.existsSync(logFile)) {
250
- console.log(chalk.gray('\n Log dosyası bulunamadı.\n'));
251
- return;
252
- }
253
-
254
- const lines = fs.readFileSync(logFile, 'utf-8').split('\n').filter(l => l.trim());
255
- const filtered = channel
256
- ? lines.filter(l => l.toLowerCase().includes(channel.toLowerCase()))
257
- : lines;
258
-
259
- const last = filtered.slice(-20);
260
- console.log('');
261
- console.log(chalk.gray(' ' + '─'.repeat(48)));
262
- console.log(chalk.cyan.bold(`\n Kanal Logları${channel ? ` (${channel})` : ''}\n`));
263
- last.forEach(l => console.log(chalk.gray(' ' + l)));
264
- console.log('');
265
- }
266
-
267
- module.exports = channels;
1
+ const chalk = require('chalk');
2
+ const F = require('../utils/format');
3
+ const { getConfig, saveConfig } = require('../utils/config');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+
8
+ async function channels(args) {
9
+ const [action, ...params] = (args || []);
10
+
11
+ if (!action || action === 'list') return listChannels();
12
+ if (action === 'status') return statusChannels();
13
+ if (action === 'remove') return removeChannel(params[0]);
14
+ if (action === 'logs') return channelLogs(params[0]);
15
+ if (action === 'add') return addChannel();
16
+ if (action === 'login') return loginChannel(params[0]);
17
+ if (action === 'logout') return logoutChannel(params[0]);
18
+ if (action === 'capabilities') return capabilitiesChannels(params[0]);
19
+ if (action === 'resolve') return resolveIdentifier(params[0], params[1]);
20
+
21
+ F.error(`Unknown command: ${action}`);
22
+ F.info('Usage: natureco channels [list|status|add|remove|login|logout|capabilities|resolve|logs]');
23
+ process.exit(1);
24
+ }
25
+
26
+ function addChannel() {
27
+ F.section('Add Channel');
28
+ F.list([
29
+ 'telegram - Connect Telegram bot',
30
+ 'whatsapp - Connect WhatsApp bot',
31
+ 'discord - Connect Discord bot',
32
+ 'slack - Connect Slack bot',
33
+ 'signal - Connect Signal bot',
34
+ 'irc - Connect IRC bot',
35
+ 'mattermost - Connect Mattermost bot',
36
+ 'imessage - Connect iMessage bridge',
37
+ 'sms - Connect SMS/Twilio',
38
+ 'webhooks - Connect webhooks',
39
+ ]);
40
+ F.info('Use: natureco <channel> connect');
41
+ }
42
+
43
+ function loginChannel(channel) {
44
+ if (!channel) {
45
+ F.error('Channel name required');
46
+ F.info('Usage: natureco channels login <telegram|whatsapp|discord|slack|signal|irc|mattermost|imessage|sms|webhooks>');
47
+ process.exit(1);
48
+ }
49
+
50
+ const config = getConfig();
51
+ const ch = channel.toLowerCase();
52
+ const channelKeys = {
53
+ telegram: 'telegramToken',
54
+ whatsapp: 'whatsappConnected',
55
+ discord: 'discordToken',
56
+ slack: 'slackToken',
57
+ signal: 'signalBotId',
58
+ irc: 'ircBotId',
59
+ mattermost: 'mattermostBotId',
60
+ imessage: 'imessageBotId',
61
+ sms: 'smsBotId',
62
+ webhooks: 'webhooks',
63
+ };
64
+
65
+ const key = channelKeys[ch];
66
+ if (!key) {
67
+ F.error(`Unknown channel: ${channel}`);
68
+ process.exit(1);
69
+ }
70
+
71
+ const isConfigured = key === 'webhooks'
72
+ ? !!(config.webhooks && config.webhooks.length > 0)
73
+ : !!config[key];
74
+
75
+ if (!isConfigured) {
76
+ F.error(`${channel} channel not configured`);
77
+ F.info(`Configure first with: natureco ${ch} connect`);
78
+ process.exit(1);
79
+ }
80
+
81
+ F.success(`${channel} session opening...`);
82
+ F.info(`Authentication and session initialization to ${channel} server.`);
83
+ }
84
+
85
+ function logoutChannel(channel) {
86
+ if (!channel) {
87
+ F.error('Channel name required');
88
+ F.info('Usage: natureco channels logout <telegram|whatsapp|discord|slack|signal|irc|mattermost|imessage|sms|webhooks>');
89
+ process.exit(1);
90
+ }
91
+
92
+ const config = getConfig();
93
+ const ch = channel.toLowerCase();
94
+
95
+ if (ch === 'telegram') {
96
+ delete config.telegramToken;
97
+ delete config.telegramBotId;
98
+ delete config.telegramAllowedChats;
99
+ } else if (ch === 'whatsapp') {
100
+ if (config.whatsappBotId) {
101
+ const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
102
+ if (fs.existsSync(sessionDir)) {
103
+ fs.rmSync(sessionDir, { recursive: true, force: true });
104
+ }
105
+ }
106
+ delete config.whatsappConnected;
107
+ delete config.whatsappBotId;
108
+ delete config.whatsappPhone;
109
+ delete config.whatsappAllowedNumbers;
110
+ } else if (ch === 'discord') {
111
+ delete config.discordToken;
112
+ delete config.discordBotId;
113
+ } else if (ch === 'slack') {
114
+ delete config.slackToken;
115
+ delete config.slackBotId;
116
+ } else if (ch === 'signal') {
117
+ delete config.signalHttpUrl;
118
+ delete config.signalAccount;
119
+ delete config.signalDmPolicy;
120
+ delete config.signalBotId;
121
+ } else if (ch === 'irc') {
122
+ delete config.ircHost; delete config.ircPort; delete config.ircTls;
123
+ delete config.ircNick; delete config.ircUsername; delete config.ircPassword;
124
+ delete config.ircChannels; delete config.ircDmPolicy; delete config.ircBotId;
125
+ } else if (ch === 'mattermost') {
126
+ delete config.mattermostBaseUrl; delete config.mattermostToken;
127
+ delete config.mattermostDmPolicy; delete config.mattermostBotId;
128
+ } else if (ch === 'imessage') {
129
+ delete config.imessageCliPath; delete config.imessageDmPolicy; delete config.imessageBotId;
130
+ } else if (ch === 'sms') {
131
+ delete config.smsAccountSid; delete config.smsAuthToken; delete config.smsFromNumber;
132
+ delete config.smsPublicWebhookUrl; delete config.smsDmPolicy; delete config.smsBotId;
133
+ } else if (ch === 'webhooks') {
134
+ delete config.webhooks; delete config.webhookEnabled;
135
+ } else {
136
+ F.error(`Unknown channel: ${channel}`);
137
+ process.exit(1);
138
+ }
139
+
140
+ saveConfig(config);
141
+ F.success(`${channel} session closed and config cleaned`);
142
+ }
143
+
144
+ function capabilitiesChannels(channel) {
145
+ const capabilities = [
146
+ { name: 'telegram', send: '✓', receive: '✓', media: '✓', polls: '✓', admin: '✓', webhooks: '✓', threads: '✓' },
147
+ { name: 'whatsapp', send: '✓', receive: '✓', media: '✓', polls: '✗', admin: '✗', webhooks: '✓', threads: '✗' },
148
+ { name: 'discord', send: '✓', receive: '✓', media: '✓', polls: '✓', admin: '✓', webhooks: '✓', threads: '✓' },
149
+ { name: 'slack', send: '✓', receive: '✓', media: '✓', polls: '✓', admin: '✓', webhooks: '✓', threads: '✓' },
150
+ { name: 'signal', send: '✓', receive: '✓', media: '✓', polls: '✗', admin: '✗', webhooks: '✗', threads: '✗' },
151
+ { name: 'irc', send: '✓', receive: '', media: '✗', polls: '✗', admin: '✗', webhooks: '✗', threads: '✗' },
152
+ { name: 'mattermost', send: '✓', receive: '✓', media: '✓', polls: '✓', admin: '✓', webhooks: '✓', threads: '✓' },
153
+ { name: 'imessage', send: '✓', receive: '✓', media: '✓', polls: '✗', admin: '✗', webhooks: '✗', threads: '✓' },
154
+ { name: 'sms', send: '✓', receive: '✓', media: '✗', polls: '✗', admin: '✗', webhooks: '✓', threads: '✗' },
155
+ { name: 'webhooks', send: '✗', receive: '✓', media: '✗', polls: '✗', admin: '✗', webhooks: '✗', threads: '✗' },
156
+ ];
157
+
158
+ const entries = channel
159
+ ? capabilities.filter(c => c.name === channel.toLowerCase())
160
+ : capabilities;
161
+
162
+ if (channel && entries.length === 0) {
163
+ F.error(`Unknown channel: ${channel}`);
164
+ process.exit(1);
165
+ }
166
+
167
+ const headers = ['Channel', 'Send', 'Recv', 'Media', 'Polls', 'Admin', 'WkHk', 'Threads'];
168
+ const rows = entries.map(c => [c.name, c.send, c.receive, c.media, c.polls, c.admin, c.webhooks, c.threads]);
169
+
170
+ F.table(headers, rows);
171
+ F.meta('WkHk = Webhook Callback support');
172
+ }
173
+
174
+ function resolveIdentifier(channel, identifier) {
175
+ if (!channel) {
176
+ F.error('Channel name required');
177
+ F.info('Usage: natureco channels resolve <channel> <user/group>');
178
+ process.exit(1);
179
+ }
180
+
181
+ if (!identifier) {
182
+ F.error('User/group identifier required');
183
+ F.info('Usage: natureco channels resolve <channel> <user/group>');
184
+ process.exit(1);
185
+ }
186
+
187
+ const knownKinds = ['telegram', 'whatsapp', 'discord', 'slack', 'signal', 'irc', 'mattermost', 'imessage', 'sms', 'webhooks'];
188
+ const ch = channel.toLowerCase();
189
+
190
+ if (!knownKinds.includes(ch)) {
191
+ F.error(`Unknown channel: ${channel}`);
192
+ process.exit(1);
193
+ }
194
+
195
+ F.header('Resolve Identifier');
196
+ F.kv('Channel', ch);
197
+ F.kv('Identifier', identifier);
198
+ F.kv('Action', `Query ${ch} API to resolve "${identifier}" to a canonical user/group ID`);
199
+ F.kv('Result', `[mock] resolved to ${ch}:${identifier.replace(/[^a-zA-Z0-9]/g, '_')}@${ch}.local`);
200
+ }
201
+
202
+ function listChannels() {
203
+ const config = getConfig();
204
+ const channels = [];
205
+
206
+ if (config.telegramToken) {
207
+ channels.push({
208
+ name: 'Telegram',
209
+ type: '✈',
210
+ status: 'connected',
211
+ detail: `token: ${config.telegramToken.slice(0, 12)}...`,
212
+ });
213
+ }
214
+
215
+ if (config.whatsappConnected) {
216
+ channels.push({
217
+ name: 'WhatsApp',
218
+ type: '📱',
219
+ status: 'connected',
220
+ detail: config.whatsappPhone ? `+${config.whatsappPhone.split(':')[0]}` : 'connected',
221
+ });
222
+ }
223
+
224
+ if (config.discordToken) {
225
+ channels.push({
226
+ name: 'Discord',
227
+ type: '🎮',
228
+ status: 'connected',
229
+ detail: `token: ${config.discordToken.slice(0, 12)}...`,
230
+ });
231
+ }
232
+
233
+ if (config.slackToken) {
234
+ channels.push({
235
+ name: 'Slack',
236
+ type: '💼',
237
+ status: 'connected',
238
+ detail: `token: ${config.slackToken.slice(0, 12)}...`,
239
+ });
240
+ }
241
+
242
+ if (config.signalBotId) {
243
+ channels.push({
244
+ name: 'Signal',
245
+ type: '🔒',
246
+ status: 'connected',
247
+ detail: `account: ${config.signalAccount || 'connected'}`,
248
+ });
249
+ }
250
+
251
+ if (config.ircBotId) {
252
+ channels.push({
253
+ name: 'IRC',
254
+ type: '💬',
255
+ status: 'connected',
256
+ detail: `${config.ircHost}:${config.ircPort} (${config.ircNick})`,
257
+ });
258
+ }
259
+
260
+ if (config.mattermostBotId) {
261
+ channels.push({
262
+ name: 'Mattermost',
263
+ type: '📢',
264
+ status: 'connected',
265
+ detail: `server: ${config.mattermostBaseUrl || 'connected'}`,
266
+ });
267
+ }
268
+
269
+ if (config.imessageBotId) {
270
+ channels.push({
271
+ name: 'iMessage',
272
+ type: '💬',
273
+ status: 'connected',
274
+ detail: 'macOS bridge',
275
+ });
276
+ }
277
+
278
+ if (config.smsBotId) {
279
+ channels.push({
280
+ name: 'SMS (Twilio)',
281
+ type: '📨',
282
+ status: 'connected',
283
+ detail: `number: ${config.smsFromNumber || 'connected'}`,
284
+ });
285
+ }
286
+
287
+ if (config.webhooks && config.webhooks.length > 0) {
288
+ channels.push({
289
+ name: 'Webhooks',
290
+ type: '🔗',
291
+ status: 'connected',
292
+ detail: `${config.webhooks.length} route`,
293
+ });
294
+ }
295
+
296
+ F.header('Channels');
297
+
298
+ if (channels.length === 0) {
299
+ F.info('No connected channels.');
300
+ F.info('To connect:');
301
+ F.list([
302
+ 'natureco telegram connect',
303
+ 'natureco whatsapp connect',
304
+ 'natureco discord connect',
305
+ 'natureco slack connect',
306
+ 'natureco signal connect',
307
+ 'natureco irc connect',
308
+ 'natureco mattermost connect',
309
+ 'natureco imessage connect',
310
+ 'natureco sms connect',
311
+ 'natureco webhooks connect',
312
+ ]);
313
+ return;
314
+ }
315
+
316
+ F.table(['Name', 'Type', 'Status', 'Detail'],
317
+ channels.map(ch => [ch.name, ch.type, ch.status, ch.detail])
318
+ );
319
+
320
+ F.meta(`Total: ${channels.length} channel(s) connected`);
321
+ F.info('To remove: natureco channels remove <channel>');
322
+ }
323
+
324
+ function statusChannels() {
325
+ const config = getConfig();
326
+
327
+ const checks = [
328
+ { name: 'Telegram', ok: !!config.telegramToken },
329
+ { name: 'WhatsApp', ok: !!config.whatsappConnected },
330
+ { name: 'Discord', ok: !!config.discordToken },
331
+ { name: 'Slack', ok: !!config.slackToken },
332
+ { name: 'Signal', ok: !!config.signalBotId },
333
+ { name: 'IRC', ok: !!config.ircBotId },
334
+ { name: 'Mattermost', ok: !!config.mattermostBotId },
335
+ { name: 'iMessage', ok: !!config.imessageBotId },
336
+ { name: 'SMS', ok: !!config.smsBotId },
337
+ { name: 'Webhooks', ok: !!(config.webhooks && config.webhooks.length > 0) },
338
+ ];
339
+
340
+ checks.forEach(ch => F.dot(ch.ok, ch.name));
341
+
342
+ const connected = checks.filter(c => c.ok).length;
343
+ F.kv('Active', `${connected}/${checks.length} channels`);
344
+ }
345
+
346
+ function removeChannel(channel) {
347
+ if (!channel) {
348
+ F.error('Channel name required');
349
+ F.info('Usage: natureco channels remove <telegram|whatsapp|discord|slack|signal|irc|mattermost|imessage|sms|webhooks>');
350
+ process.exit(1);
351
+ }
352
+
353
+ const config = getConfig();
354
+ const ch = channel.toLowerCase();
355
+
356
+ if (ch === 'telegram') {
357
+ delete config.telegramToken;
358
+ delete config.telegramBotId;
359
+ delete config.telegramAllowedChats;
360
+ } else if (ch === 'whatsapp') {
361
+ if (config.whatsappBotId) {
362
+ const sessionDir = path.join(os.homedir(), '.natureco', 'whatsapp-sessions', config.whatsappBotId);
363
+ if (fs.existsSync(sessionDir)) {
364
+ fs.rmSync(sessionDir, { recursive: true, force: true });
365
+ }
366
+ }
367
+ delete config.whatsappConnected;
368
+ delete config.whatsappBotId;
369
+ delete config.whatsappPhone;
370
+ delete config.whatsappAllowedNumbers;
371
+ } else if (ch === 'discord') {
372
+ delete config.discordToken;
373
+ delete config.discordBotId;
374
+ } else if (ch === 'slack') {
375
+ delete config.slackToken;
376
+ delete config.slackBotId;
377
+ } else if (ch === 'signal') {
378
+ delete config.signalHttpUrl;
379
+ delete config.signalAccount;
380
+ delete config.signalDmPolicy;
381
+ delete config.signalBotId;
382
+ } else if (ch === 'irc') {
383
+ delete config.ircHost; delete config.ircPort; delete config.ircTls;
384
+ delete config.ircNick; delete config.ircUsername; delete config.ircPassword;
385
+ delete config.ircChannels; delete config.ircDmPolicy; delete config.ircBotId;
386
+ } else if (ch === 'mattermost') {
387
+ delete config.mattermostBaseUrl; delete config.mattermostToken;
388
+ delete config.mattermostDmPolicy; delete config.mattermostBotId;
389
+ } else if (ch === 'imessage') {
390
+ delete config.imessageCliPath; delete config.imessageDmPolicy; delete config.imessageBotId;
391
+ } else if (ch === 'sms') {
392
+ delete config.smsAccountSid; delete config.smsAuthToken; delete config.smsFromNumber;
393
+ delete config.smsPublicWebhookUrl; delete config.smsDmPolicy; delete config.smsBotId;
394
+ } else if (ch === 'webhooks') {
395
+ delete config.webhooks; delete config.webhookEnabled;
396
+ } else {
397
+ F.error(`Unknown channel: ${channel}`);
398
+ process.exit(1);
399
+ }
400
+
401
+ saveConfig(config);
402
+ F.success(`${channel} removed`);
403
+ }
404
+
405
+ function channelLogs(channel) {
406
+ const logFile = path.join(os.homedir(), '.natureco', 'gateway.log');
407
+ if (!fs.existsSync(logFile)) {
408
+ F.meta('Log file not found.');
409
+ return;
410
+ }
411
+
412
+ const lines = fs.readFileSync(logFile, 'utf-8').split('\n').filter(l => l.trim());
413
+ const filtered = channel
414
+ ? lines.filter(l => l.toLowerCase().includes(channel.toLowerCase()))
415
+ : lines;
416
+
417
+ const last = filtered.slice(-20);
418
+ F.header(`Channel Logs${channel ? ` (${channel})` : ''}`);
419
+ last.forEach(l => F.meta(l));
420
+ }
421
+
422
+ module.exports = channels;