natureco-cli 2.23.29 → 2.23.31

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 (111) hide show
  1. package/README.md +94 -11
  2. package/bin/natureco.js +495 -94
  3. package/package.json +1 -1
  4. package/src/commands/acp.js +39 -0
  5. package/src/commands/admin-rpc.js +302 -0
  6. package/src/commands/agent.js +280 -0
  7. package/src/commands/agents.js +114 -30
  8. package/src/commands/approvals.js +214 -0
  9. package/src/commands/backup.js +124 -0
  10. package/src/commands/bonjour.js +167 -0
  11. package/src/commands/browser.js +815 -0
  12. package/src/commands/capability.js +237 -0
  13. package/src/commands/channels.js +422 -267
  14. package/src/commands/chat.js +5 -8
  15. package/src/commands/clawbot.js +19 -0
  16. package/src/commands/clickclack.js +130 -0
  17. package/src/commands/code.js +3 -2
  18. package/src/commands/commitments.js +148 -0
  19. package/src/commands/completion.js +84 -0
  20. package/src/commands/config.js +219 -30
  21. package/src/commands/configure.js +110 -0
  22. package/src/commands/crestodian.js +92 -0
  23. package/src/commands/cron.js +239 -19
  24. package/src/commands/daemon.js +90 -0
  25. package/src/commands/dashboard.js +47 -374
  26. package/src/commands/device-pair.js +248 -0
  27. package/src/commands/devices.js +137 -0
  28. package/src/commands/directory.js +179 -0
  29. package/src/commands/dns.js +196 -0
  30. package/src/commands/docs.js +136 -0
  31. package/src/commands/doctor.js +143 -492
  32. package/src/commands/exec-policy.js +80 -0
  33. package/src/commands/gateway-server.js +1155 -24
  34. package/src/commands/gateway.js +492 -249
  35. package/src/commands/health.js +148 -0
  36. package/src/commands/help.js +24 -25
  37. package/src/commands/hooks.js +141 -87
  38. package/src/commands/imessage.js +128 -14
  39. package/src/commands/infer.js +1474 -0
  40. package/src/commands/irc.js +64 -15
  41. package/src/commands/logs.js +122 -99
  42. package/src/commands/mattermost.js +114 -12
  43. package/src/commands/mcp.js +121 -309
  44. package/src/commands/memory-cmd.js +134 -1
  45. package/src/commands/memory.js +128 -0
  46. package/src/commands/message.js +720 -134
  47. package/src/commands/migrate.js +213 -2
  48. package/src/commands/models.js +39 -1
  49. package/src/commands/node.js +98 -0
  50. package/src/commands/nodes.js +362 -0
  51. package/src/commands/oc-path.js +200 -0
  52. package/src/commands/onboard.js +129 -0
  53. package/src/commands/open-prose.js +67 -0
  54. package/src/commands/pairing.js +108 -107
  55. package/src/commands/path.js +206 -0
  56. package/src/commands/plugins.js +35 -1
  57. package/src/commands/policy.js +176 -0
  58. package/src/commands/proxy.js +306 -0
  59. package/src/commands/qr.js +70 -0
  60. package/src/commands/reset.js +101 -94
  61. package/src/commands/sandbox.js +125 -0
  62. package/src/commands/secrets.js +201 -0
  63. package/src/commands/sessions.js +110 -51
  64. package/src/commands/setup.js +102 -543
  65. package/src/commands/signal.js +447 -18
  66. package/src/commands/skills.js +67 -1
  67. package/src/commands/sms.js +123 -19
  68. package/src/commands/status.js +101 -127
  69. package/src/commands/system.js +53 -0
  70. package/src/commands/tasks.js +208 -100
  71. package/src/commands/terminal.js +139 -0
  72. package/src/commands/thread-ownership.js +157 -0
  73. package/src/commands/transcripts.js +95 -0
  74. package/src/commands/tui.js +41 -0
  75. package/src/commands/uninstall.js +73 -92
  76. package/src/commands/update.js +146 -91
  77. package/src/commands/voice.js +82 -0
  78. package/src/commands/vydra.js +98 -0
  79. package/src/commands/webhooks.js +58 -66
  80. package/src/commands/wiki.js +783 -0
  81. package/src/commands/workboard.js +207 -0
  82. package/src/tools/audio_understanding.js +154 -0
  83. package/src/tools/browser.js +112 -0
  84. package/src/tools/canvas.js +104 -0
  85. package/src/tools/document_extract.js +84 -0
  86. package/src/tools/duckduckgo.js +54 -0
  87. package/src/tools/exa_search.js +66 -0
  88. package/src/tools/firecrawl.js +104 -0
  89. package/src/tools/image_generation.js +99 -0
  90. package/src/tools/llm_task.js +118 -0
  91. package/src/tools/media_understanding.js +128 -0
  92. package/src/tools/music_generation.js +113 -0
  93. package/src/tools/parallel_search.js +77 -0
  94. package/src/tools/phone_control.js +80 -0
  95. package/src/tools/phone_control_enhanced.js +184 -0
  96. package/src/tools/searxng.js +61 -0
  97. package/src/tools/speech_to_text.js +135 -0
  98. package/src/tools/text_to_speech.js +105 -0
  99. package/src/tools/thread_ownership.js +88 -0
  100. package/src/tools/video_generation.js +72 -0
  101. package/src/tools/web_readability.js +104 -0
  102. package/src/utils/agents-md.js +85 -0
  103. package/src/utils/api.js +39 -40
  104. package/src/utils/format.js +144 -0
  105. package/src/utils/headless.js +2 -1
  106. package/src/utils/memory.js +200 -0
  107. package/src/utils/parallel-tools.js +106 -0
  108. package/src/utils/sub-agent.js +148 -0
  109. package/src/utils/token-budget.js +304 -0
  110. package/src/utils/tool-runner.js +7 -5
  111. package/src/utils/web-fetch.js +107 -0
@@ -1,8 +1,39 @@
1
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');
2
6
  const inquirer = require('../utils/inquirer-wrapper');
3
7
  const { getConfig, saveConfig } = require('../utils/config');
4
8
  const { getBots } = require('../utils/api');
5
9
 
10
+ const BINDINGS_FILE = path.join(os.homedir(), '.natureco', 'agent-bindings.json');
11
+ const IDENTITIES_FILE = path.join(os.homedir(), '.natureco', 'agent-identities.json');
12
+
13
+ function loadBindings() {
14
+ try {
15
+ if (fs.existsSync(BINDINGS_FILE)) return JSON.parse(fs.readFileSync(BINDINGS_FILE, 'utf8'));
16
+ } catch {}
17
+ return {};
18
+ }
19
+
20
+ function saveBindings(data) {
21
+ fs.mkdirSync(path.dirname(BINDINGS_FILE), { recursive: true });
22
+ fs.writeFileSync(BINDINGS_FILE, JSON.stringify(data, null, 2));
23
+ }
24
+
25
+ function loadIdentities() {
26
+ try {
27
+ if (fs.existsSync(IDENTITIES_FILE)) return JSON.parse(fs.readFileSync(IDENTITIES_FILE, 'utf8'));
28
+ } catch {}
29
+ return {};
30
+ }
31
+
32
+ function saveIdentities(data) {
33
+ fs.mkdirSync(path.dirname(IDENTITIES_FILE), { recursive: true });
34
+ fs.writeFileSync(IDENTITIES_FILE, JSON.stringify(data, null, 2));
35
+ }
36
+
6
37
  async function agents(args) {
7
38
  const [action, ...params] = (args || []);
8
39
 
@@ -10,9 +41,13 @@ async function agents(args) {
10
41
  if (action === 'set') return setActiveAgent(params[0]);
11
42
  if (action === 'info') return agentInfo(params[0]);
12
43
  if (action === 'add') return addAgent();
44
+ if (action === 'bindings') return listBindings();
45
+ if (action === 'bind') return bindAgent(params[0], params[1]);
46
+ if (action === 'unbind') return unbindAgent(params[0], params[1]);
47
+ if (action === 'set-identity') return setIdentity(params[0], params.slice(1).join(' '));
13
48
 
14
49
  console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
15
- console.log(chalk.gray(' Kullanım: natureco agents [list|set|info|add]\n'));
50
+ console.log(chalk.gray(' Kullanım: natureco agents [list|set|info|add|bindings|bind|unbind|set-identity]\n'));
16
51
  process.exit(1);
17
52
  }
18
53
 
@@ -20,32 +55,28 @@ async function listAgents() {
20
55
  const config = getConfig();
21
56
  const apiKey = config.providerApiKey || config.apiKey || '';
22
57
 
23
- console.log(chalk.gray('\n Agentlar yükleniyor...\n'));
58
+ F.info('Agentlar yükleniyor...');
24
59
 
25
60
  let botList = { bots: [] };
26
61
  try {
27
62
  botList = await getBots(apiKey);
28
63
  } catch {}
29
64
 
30
- console.log(chalk.gray(' ' + '─'.repeat(48)));
31
- console.log(chalk.cyan.bold('\n Agentlar (Botlar)\n'));
65
+ F.header('Agents');
32
66
 
33
67
  if (!botList.bots?.length) {
34
- console.log(chalk.gray(' Agent bulunamadı.'));
35
- console.log(chalk.gray(' Oluşturmak için: ') + chalk.cyan('developers.natureco.me\n'));
68
+ F.list(['Agent bulunamadı.', 'Oluşturmak için: developers.natureco.me']);
36
69
  return;
37
70
  }
38
71
 
39
- botList.bots.forEach((bot, i) => {
40
- const active = config.botName === bot.name ? chalk.green(' ← aktif') : '';
41
- console.log(chalk.white(` ${i + 1}. ${bot.name}`) + active);
42
- console.log(chalk.gray(` ID : ${bot.id}`));
43
- console.log(chalk.gray(` Provider: ${bot.ai_provider || 'groq'}`));
44
- if (bot.model) console.log(chalk.gray(` Model : ${bot.model}`));
45
- console.log('');
46
- });
72
+ const rows = botList.bots.map(bot => [
73
+ bot.name + (config.botName === bot.name ? ' ← aktif' : ''),
74
+ bot.id,
75
+ bot.ai_provider || 'groq',
76
+ bot.model || ''
77
+ ]);
78
+ F.table(['Name', 'ID', 'Provider', 'Model'], rows);
47
79
 
48
- console.log(chalk.gray(' ' + '─'.repeat(48)));
49
80
  console.log(chalk.gray(' Değiştirmek için: ') + chalk.cyan('natureco agents set <bot-adı>\n'));
50
81
  }
51
82
 
@@ -91,7 +122,7 @@ async function agentInfo(botName) {
91
122
  const name = botName || config.botName;
92
123
 
93
124
  if (!name) {
94
- console.log(chalk.gray('\n Agent adı belirtin: natureco agents info <bot-adı>\n'));
125
+ F.error('Agent adı belirtin: natureco agents info <bot-adı>');
95
126
  return;
96
127
  }
97
128
 
@@ -102,33 +133,27 @@ async function agentInfo(botName) {
102
133
 
103
134
  const bot = botList.bots?.find(b => b.name === name || b.id === name);
104
135
 
105
- console.log('');
106
- console.log(chalk.gray(' ' + '─'.repeat(48)));
107
- console.log(chalk.cyan.bold(`\n Agent: ${name}\n`));
136
+ F.header('Agent: ' + name);
108
137
 
109
138
  if (bot) {
110
- console.log(chalk.gray(' ID : ') + chalk.white(bot.id));
111
- console.log(chalk.gray(' Provider: ') + chalk.white(bot.ai_provider || 'groq'));
112
- if (bot.model) console.log(chalk.gray(' Model : ') + chalk.white(bot.model));
139
+ F.kv('ID', bot.id);
140
+ F.kv('Provider', bot.ai_provider || 'groq');
141
+ if (bot.model) F.kv('Model', bot.model);
113
142
  if (bot.system_prompt) {
114
- console.log(chalk.gray(' Prompt : ') + chalk.white(bot.system_prompt.slice(0, 80) + (bot.system_prompt.length > 80 ? '...' : '')));
143
+ F.kv('Prompt', bot.system_prompt.slice(0, 80) + (bot.system_prompt.length > 80 ? '...' : ''));
115
144
  }
116
145
  } else {
117
- console.log(chalk.gray(' (Detay alınamadı)'));
146
+ F.meta('(Detay alınamadı)');
118
147
  }
119
148
 
120
- // Hafıza bilgisi
121
149
  try {
122
150
  const { loadMemory } = require('../utils/memory');
123
151
  const mem = loadMemory(bot?.id || 'universal-provider');
124
152
  if (mem.name || mem.facts?.length) {
125
- console.log('');
126
- if (mem.name) console.log(chalk.gray(' Kullanıcı: ') + chalk.white(mem.name));
127
- console.log(chalk.gray(' Hafıza : ') + chalk.white(`${(mem.facts || []).length} bilgi`));
153
+ if (mem.name) F.kv('Kullanıcı', mem.name);
154
+ F.kv('Hafıza', (mem.facts || []).length + ' bilgi');
128
155
  }
129
156
  } catch {}
130
-
131
- console.log('');
132
157
  }
133
158
 
134
159
  async function addAgent() {
@@ -138,4 +163,63 @@ async function addAgent() {
138
163
  console.log(chalk.gray(' Oluşturduktan sonra: ') + chalk.cyan('natureco agents list\n'));
139
164
  }
140
165
 
166
+ // ── Bindings ──────────────────────────────────────────────────────────────────
167
+
168
+ function listBindings() {
169
+ const bindings = loadBindings();
170
+ const keys = Object.keys(bindings);
171
+
172
+ F.section('Bindings (' + keys.length + ')');
173
+
174
+ if (keys.length === 0) {
175
+ F.meta('Henüz binding yok.');
176
+ return;
177
+ }
178
+
179
+ const items = [];
180
+ keys.forEach(agentId => {
181
+ bindings[agentId].forEach(ch => items.push(agentId + ' → ' + ch));
182
+ });
183
+ F.list(items);
184
+ }
185
+
186
+ function bindAgent(agentId, channel) {
187
+ if (!agentId || !channel) {
188
+ F.error('Kullanım: natureco agents bind <agentId> <channel>');
189
+ return;
190
+ }
191
+ const bindings = loadBindings();
192
+ if (!bindings[agentId]) bindings[agentId] = [];
193
+ if (!bindings[agentId].includes(channel)) bindings[agentId].push(channel);
194
+ saveBindings(bindings);
195
+ F.success('Agent ' + agentId + ' → ' + channel + ' bağlandı');
196
+ }
197
+
198
+ function unbindAgent(agentId, channel) {
199
+ if (!agentId || !channel) {
200
+ F.error('Kullanım: natureco agents unbind <agentId> <channel>');
201
+ return;
202
+ }
203
+ const bindings = loadBindings();
204
+ if (!bindings[agentId]) {
205
+ F.warning('Agent ' + agentId + ' için binding bulunamadı');
206
+ return;
207
+ }
208
+ bindings[agentId] = bindings[agentId].filter(ch => ch !== channel);
209
+ if (bindings[agentId].length === 0) delete bindings[agentId];
210
+ saveBindings(bindings);
211
+ F.success('Agent ' + agentId + ' → ' + channel + ' bağlantısı kaldırıldı');
212
+ }
213
+
214
+ function setIdentity(agentId, identity) {
215
+ if (!agentId) {
216
+ F.error('Kullanım: natureco agents set-identity <agentId> <identity>');
217
+ return;
218
+ }
219
+ const identities = loadIdentities();
220
+ identities[agentId] = identity || 'default';
221
+ saveIdentities(identities);
222
+ F.success('Agent ' + agentId + ' identity: ' + (identity || 'default'));
223
+ }
224
+
141
225
  module.exports = agents;
@@ -0,0 +1,214 @@
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 { getConfig, saveConfig } = require('../utils/config');
7
+
8
+ const APPROVALS_FILE = path.join(os.homedir(), '.natureco', 'approvals.json');
9
+ const ALLOWLIST_FILE = path.join(os.homedir(), '.natureco', 'approval-allowlist.json');
10
+
11
+ function loadQueue() {
12
+ if (!fs.existsSync(APPROVALS_FILE)) return [];
13
+ try { return JSON.parse(fs.readFileSync(APPROVALS_FILE, 'utf-8')); }
14
+ catch { return []; }
15
+ }
16
+
17
+ function saveQueue(items) {
18
+ const dir = path.dirname(APPROVALS_FILE);
19
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
20
+ fs.writeFileSync(APPROVALS_FILE, JSON.stringify(items, null, 2), 'utf-8');
21
+ }
22
+
23
+ function genId() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
24
+
25
+ function loadAllowlist() {
26
+ if (!fs.existsSync(ALLOWLIST_FILE)) return [];
27
+ try { return JSON.parse(fs.readFileSync(ALLOWLIST_FILE, 'utf-8')); }
28
+ catch { return []; }
29
+ }
30
+
31
+ function saveAllowlist(patterns) {
32
+ const dir = path.dirname(ALLOWLIST_FILE);
33
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
34
+ fs.writeFileSync(ALLOWLIST_FILE, JSON.stringify(patterns, null, 2), 'utf-8');
35
+ }
36
+
37
+ function approvals(args) {
38
+ const [action, ...params] = args || [];
39
+
40
+ if (!action || action === 'list') return listApprovals();
41
+ if (action === 'pending') return listPending();
42
+ if (action === 'approve') return approveReq(params[0]);
43
+ if (action === 'reject') return rejectReq(params[0]);
44
+ if (action === 'set-policy') return setPolicy(params[0]);
45
+ if (action === 'add') return addReq(params.join(' '));
46
+ if (action === 'get') return getReq(params[0]);
47
+ if (action === 'set') return setPolicy(params[0]);
48
+ if (action === 'allowlist' && params[0] === 'add') return allowlistAdd(params.slice(1).join(' '));
49
+ if (action === 'allowlist' && params[0] === 'remove') return allowlistRemove(params.slice(1).join(' '));
50
+ if (action === 'allowlist') return listAllowlist();
51
+
52
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
53
+ console.log(chalk.gray(' Kullanım: natureco approvals [list|pending|approve|reject|set-policy|set|add|get|allowlist]\n'));
54
+ process.exit(1);
55
+ }
56
+
57
+ function listApprovals() {
58
+ const config = getConfig();
59
+ const policy = config.execApprovalPolicy || 'auto';
60
+ const queue = loadQueue();
61
+
62
+ F.header('Approvals');
63
+ F.kv('Policy', policy);
64
+ F.kv('Queue', `${queue.filter(r => r.status === 'pending').length} pending`);
65
+
66
+ const rows = queue.map(r => [
67
+ r.id,
68
+ r.source || 'cli',
69
+ r.text,
70
+ r.status,
71
+ ]);
72
+ F.table(['ID', 'Requester', 'Command', 'Status'], rows);
73
+ }
74
+
75
+ function listPending() {
76
+ const queue = loadQueue().filter(r => r.status === 'pending');
77
+ if (queue.length === 0) {
78
+ F.info('No pending approvals.');
79
+ return;
80
+ }
81
+
82
+ const rows = queue.map(r => [
83
+ r.id,
84
+ r.source || 'cli',
85
+ r.text,
86
+ r.status,
87
+ ]);
88
+ F.table(['ID', 'Requester', 'Command', 'Status'], rows);
89
+ }
90
+
91
+ function addReq(text) {
92
+ if (!text) {
93
+ console.log(chalk.red('\n ❌ Request text gerekli\n'));
94
+ process.exit(1);
95
+ }
96
+ const queue = loadQueue();
97
+ const r = { id: genId(), text, status: 'pending', source: 'cli', createdAt: new Date().toISOString(), resolvedAt: null };
98
+ queue.push(r);
99
+ saveQueue(queue);
100
+ console.log(chalk.yellow(`\n ⏳ Onay beklemede: ${r.id}\n`));
101
+ console.log(` ${chalk.white(text)}`);
102
+ console.log(chalk.gray(` Onaylamak için: natureco approvals approve ${r.id}`));
103
+ console.log();
104
+ }
105
+
106
+ function approveReq(id) {
107
+ const queue = loadQueue();
108
+ if (id) {
109
+ const r = queue.find(x => x.id === id);
110
+ if (!r) { console.log(chalk.red(`\n ❌ Request bulunamadı: ${id}\n`)); process.exit(1); }
111
+ if (r.status !== 'pending') { console.log(chalk.yellow(`\n ⚠️ "${id}" zaten ${r.status}\n`)); return; }
112
+ r.status = 'approved';
113
+ r.resolvedAt = new Date().toISOString();
114
+ saveQueue(queue);
115
+ F.success(`Approved: ${r.text}`);
116
+ return;
117
+ }
118
+ const pending = queue.filter(r => r.status === 'pending');
119
+ if (pending.length === 0) { console.log(chalk.gray('\n Bekleyen onay yok\n')); return; }
120
+ for (const r of pending) { r.status = 'approved'; r.resolvedAt = new Date().toISOString(); }
121
+ saveQueue(queue);
122
+ F.success(`${pending.length} requests approved`);
123
+ }
124
+
125
+ function rejectReq(id) {
126
+ const queue = loadQueue();
127
+ if (id) {
128
+ const r = queue.find(x => x.id === id);
129
+ if (!r) { console.log(chalk.red(`\n ❌ Request bulunamadı: ${id}\n`)); process.exit(1); }
130
+ if (r.status !== 'pending') { console.log(chalk.yellow(`\n ⚠️ "${id}" zaten ${r.status}\n`)); return; }
131
+ r.status = 'rejected';
132
+ r.resolvedAt = new Date().toISOString();
133
+ saveQueue(queue);
134
+ F.error(`Rejected: ${r.text}`);
135
+ return;
136
+ }
137
+ const pending = queue.filter(r => r.status === 'pending');
138
+ if (pending.length === 0) { console.log(chalk.gray('\n Bekleyen onay yok\n')); return; }
139
+ for (const r of pending) { r.status = 'rejected'; r.resolvedAt = new Date().toISOString(); }
140
+ saveQueue(queue);
141
+ F.error(`${pending.length} requests rejected`);
142
+ }
143
+
144
+ function setPolicy(policy) {
145
+ if (!policy || !['auto', 'always-ask', 'trusted'].includes(policy)) {
146
+ console.log(chalk.red('\n ❌ Policy must be: auto, always-ask, or trusted\n'));
147
+ process.exit(1);
148
+ }
149
+ const config = getConfig();
150
+ config.execApprovalPolicy = policy;
151
+ saveConfig(config);
152
+ console.log(chalk.green(`\n ✅ Approval policy: ${policy}\n`));
153
+ }
154
+
155
+ function getReq(id) {
156
+ if (!id) {
157
+ console.log(chalk.red('\n ❌ Request ID gerekli\n'));
158
+ process.exit(1);
159
+ }
160
+ const queue = loadQueue();
161
+ const r = queue.find(x => x.id === id);
162
+ if (!r) {
163
+ console.log(chalk.red(`\n ❌ Request bulunamadı: ${id}\n`));
164
+ process.exit(1);
165
+ }
166
+ F.kv('ID', r.id);
167
+ F.kv('Text', r.text);
168
+ F.kv('Status', r.status);
169
+ F.kv('Source', r.source || 'cli');
170
+ F.kv('Created', r.createdAt ? new Date(r.createdAt).toLocaleString() : '-');
171
+ F.kv('Resolved', r.resolvedAt ? new Date(r.resolvedAt).toLocaleString() : '-');
172
+ }
173
+
174
+ function listAllowlist() {
175
+ const patterns = loadAllowlist();
176
+ if (patterns.length === 0) {
177
+ F.info('No patterns in allowlist.');
178
+ return;
179
+ }
180
+ F.list(patterns);
181
+ }
182
+
183
+ function allowlistAdd(pattern) {
184
+ if (!pattern) {
185
+ console.log(chalk.red('\n ❌ Pattern gerekli\n'));
186
+ process.exit(1);
187
+ }
188
+ const patterns = loadAllowlist();
189
+ if (patterns.includes(pattern)) {
190
+ F.warning(`Pattern already in allowlist: ${pattern}`);
191
+ return;
192
+ }
193
+ patterns.push(pattern);
194
+ saveAllowlist(patterns);
195
+ F.success(`Added to allowlist: ${pattern}`);
196
+ }
197
+
198
+ function allowlistRemove(pattern) {
199
+ if (!pattern) {
200
+ console.log(chalk.red('\n ❌ Pattern gerekli\n'));
201
+ process.exit(1);
202
+ }
203
+ let patterns = loadAllowlist();
204
+ const initial = patterns.length;
205
+ patterns = patterns.filter(p => p !== pattern);
206
+ if (patterns.length === initial) {
207
+ F.warning(`Pattern not found in allowlist: ${pattern}`);
208
+ return;
209
+ }
210
+ saveAllowlist(patterns);
211
+ F.success(`Removed from allowlist: ${pattern}`);
212
+ }
213
+
214
+ module.exports = approvals;
@@ -0,0 +1,124 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { execSync } = require('child_process');
6
+
7
+ const NATURECO_DIR = path.join(os.homedir(), '.natureco');
8
+
9
+ function backup(args) {
10
+ const [action, ...params] = args || [];
11
+
12
+ if (!action || action === 'create') return createBackup();
13
+ if (action === 'list') return listBackups();
14
+ if (action === 'restore') return restoreBackup(params.join(' '));
15
+ if (action === 'verify') return verifyBackup(params.join(' '));
16
+
17
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
18
+ console.log(chalk.gray(' Kullanım: natureco backup [create|list|restore|verify]\n'));
19
+ process.exit(1);
20
+ }
21
+
22
+ function createBackup() {
23
+ const backupDir = path.join(os.homedir(), '.natureco-backups');
24
+ if (!fs.existsSync(backupDir)) fs.mkdirSync(backupDir, { recursive: true });
25
+
26
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
27
+ const backupFile = path.join(backupDir, `natureco-backup-${timestamp}.tar.gz`);
28
+
29
+ console.log(chalk.cyan('\n 💾 Creating backup...\n'));
30
+
31
+ if (fs.existsSync(NATURECO_DIR)) {
32
+ try {
33
+ execSync(`tar -czf "${backupFile}" -C "${path.dirname(NATURECO_DIR)}" "${path.basename(NATURECO_DIR)}"`, {
34
+ stdio: 'pipe',
35
+ encoding: 'utf8',
36
+ timeout: 30000
37
+ });
38
+ const size = fs.statSync(backupFile).size;
39
+ console.log(chalk.green(` ✅ Backup created: ${backupFile}`));
40
+ console.log(chalk.gray(` Size: ${(size / 1024).toFixed(1)} KB`));
41
+ } catch (err) {
42
+ console.log(chalk.yellow(' ⚠️ tar backup failed, trying copy...'));
43
+ const fallbackFile = backupFile.replace('.tar.gz', '.json');
44
+ const config = require('../utils/config').getConfig();
45
+ fs.writeFileSync(fallbackFile, JSON.stringify(config, null, 2));
46
+ console.log(chalk.green(` ✅ Config backup: ${fallbackFile}`));
47
+ }
48
+ } else {
49
+ console.log(chalk.yellow(' ⚠️ No NatureCo directory found\n'));
50
+ }
51
+ console.log();
52
+ }
53
+
54
+ function listBackups() {
55
+ const backupDir = path.join(os.homedir(), '.natureco-backups');
56
+ console.log(chalk.cyan('\n 📋 Backups\n'));
57
+ console.log(chalk.gray(' ' + '─'.repeat(48)));
58
+
59
+ if (!fs.existsSync(backupDir)) {
60
+ console.log(chalk.gray(' No backups found.\n'));
61
+ console.log(chalk.gray(' Create one: ') + chalk.cyan('natureco backup create\n'));
62
+ return;
63
+ }
64
+
65
+ const files = fs.readdirSync(backupDir).filter(f => f.startsWith('natureco-backup'));
66
+ if (files.length === 0) {
67
+ console.log(chalk.gray(' No backups found.\n'));
68
+ return;
69
+ }
70
+
71
+ for (const f of files.sort().reverse()) {
72
+ const stat = fs.statSync(path.join(backupDir, f));
73
+ const size = (stat.size / 1024).toFixed(1);
74
+ const date = stat.birthtime.toLocaleString();
75
+ console.log(` ${chalk.cyan('●')} ${chalk.white(f)} ${chalk.gray(`(${size} KB, ${date})`)}`);
76
+ }
77
+ console.log();
78
+ }
79
+
80
+ function restoreBackup(file) {
81
+ if (!file) {
82
+ console.log(chalk.red('\n ❌ Backup file gerekli\n'));
83
+ console.log(chalk.cyan(' natureco backup list'));
84
+ console.log(chalk.cyan(' natureco backup restore <filename>\n'));
85
+ process.exit(1);
86
+ }
87
+
88
+ const backupDir = path.join(os.homedir(), '.natureco-backups');
89
+ const backupFile = path.join(backupDir, file);
90
+
91
+ if (!fs.existsSync(backupFile)) {
92
+ console.log(chalk.red(`\n ❌ Backup bulunamadı: ${backupFile}\n`));
93
+ process.exit(1);
94
+ }
95
+
96
+ console.log(chalk.yellow(`\n ⚠️ Restoring ${file}...\n`));
97
+ try {
98
+ execSync(`tar -xzf "${backupFile}" -C "${os.homedir()}"`, { stdio: 'pipe', timeout: 15000 });
99
+ console.log(chalk.green(' ✅ Restore complete\n'));
100
+ } catch {
101
+ console.log(chalk.red(' ❌ Restore failed\n'));
102
+ process.exit(1);
103
+ }
104
+ }
105
+
106
+ function verifyBackup(file) {
107
+ if (!file) {
108
+ console.log(chalk.red('\n ❌ Backup file gerekli\n'));
109
+ process.exit(1);
110
+ }
111
+
112
+ const backupDir = path.join(os.homedir(), '.natureco-backups');
113
+ const backupFile = path.join(backupDir, file);
114
+
115
+ if (!fs.existsSync(backupFile)) {
116
+ console.log(chalk.red(`\n ❌ Backup bulunamadı: ${backupFile}\n`));
117
+ process.exit(1);
118
+ }
119
+
120
+ const stat = fs.statSync(backupFile);
121
+ console.log(chalk.green(`\n ✅ Backup verified: ${file} (${(stat.size / 1024).toFixed(1)} KB)\n`));
122
+ }
123
+
124
+ module.exports = backup;