natureco-cli 5.20.4 → 5.21.0

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.
@@ -101,7 +101,6 @@ function rebuildSystemPrompt(opts) {
101
101
 
102
102
  // ── Tool Guardrails instance (Hermes-style) ─────────────────────────────
103
103
  const guardrails = new ToolGuardrails();
104
- const noopCallback = () => {};
105
104
 
106
105
  // CLI komutları (REPL içinden çalıştırılabilir)
107
106
  const CLI_COMMANDS = {
@@ -119,31 +118,31 @@ const CLI_COMMANDS = {
119
118
  '/models': { desc: 'Modeller', run: ['models', 'list'] },
120
119
  '/memory-ls': { desc: 'Memory dosyaları', run: ['memory', 'list'] },
121
120
  '/seo': { desc: 'SEO denetimi (URL gerek)', needsArg: true, run: ['seo', 'audit'] },
122
- '/naturehub': { desc: 'NatureHub işlemleri (post|dm|inbox|forum|list)', needsArg: true, run: ['naturehub', 'post'] },
121
+ '/naturehub': { desc: 'Bota mesaj gönder (text gerek)', needsArg: true, run: ['naturehub', 'post'] },
123
122
  '/dashboard': { desc: 'Web dashboard başlat (port 7421)', run: ['dashboard', 'start'] },
124
123
  };
125
124
 
126
- const MEMORY_DIR = path.join(os.homedir(), '.natureco', 'memory');
127
- const SESSION_DIR = path.join(os.homedir(), '.natureco', 'sessions');
128
- const SESSIONS_INDEX = path.join(os.homedir(), '.natureco', 'sessions.json');
129
- const REPL_STATE = path.join(os.homedir(), '.natureco', 'repl-state.json');
125
+ // Profil desteği: --profile <ad> ile ~/.natureco-<ad> kullanılır (config ile tutarlı)
126
+ const { CONFIG_DIR: PROFILE_DIR } = require('../utils/config');
127
+ const MEMORY_DIR = path.join(PROFILE_DIR, 'memory');
128
+ const SESSION_DIR = path.join(PROFILE_DIR, 'sessions');
129
+ const SESSIONS_INDEX = path.join(PROFILE_DIR, 'sessions.json');
130
+ const REPL_STATE = path.join(PROFILE_DIR, 'repl-state.json');
130
131
 
131
132
  function ensureDir(dir) {
132
133
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
133
134
  }
134
135
 
135
136
  function getConfig() {
137
+ // Merkezi config'e delege — --profile desteği, backup ve validation
138
+ // yerel kopyada yoktu; REPL kullanıcının gerçek config'ini okuyordu
136
139
  try {
137
- return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.natureco', 'config.json'), 'utf8'));
140
+ return require('../utils/config').getConfig() || {};
138
141
  } catch { return {}; }
139
142
  }
140
143
 
141
- function isMiniMax(url) {
142
- return url && (url.includes('minimax.io') || url.includes('minimaxi.com') || url.includes('minimax.cn'));
143
- }
144
- function isGemini(url) {
145
- return url && (url.includes('generativelanguage.googleapis.com') || url.includes('gemini'));
146
- }
144
+ // Tek doğruluk kaynağı: provider-detect (MiniMax /v1 toleransı dahil)
145
+ const { isMiniMax, isGemini, buildChatEndpoint } = require('../utils/provider-detect');
147
146
 
148
147
  function loadMemory(username) {
149
148
  const file = path.join(MEMORY_DIR, `${(username || 'default').toLowerCase()}.json`);
@@ -197,8 +196,6 @@ function saveSession(messages, meta) {
197
196
  return id;
198
197
  }
199
198
 
200
-
201
-
202
199
  function loadSession(id) {
203
200
  // ID veya index
204
201
  const idx = loadSessionsIndex();
@@ -227,12 +224,10 @@ function extractFacts(messages, currentFacts) {
227
224
 
228
225
  // İsim/tercih pattern'leri
229
226
  const patterns = [
230
- { re: /(?:benim ad[ıi]m|ad[ıi]m|ad[ıi]n|ad[ıi]n[ıi]z|ismim|isim|I'm called|my name is)\s+([A-ZÇĞİÖŞÜa-zçğıöşü]+)/i, val: m => `Adı: ${m[1]}` },
231
- { re: /(?:yaşıyorum|yaşadığım|oturuyorum|I live in)\s+([A-ZÇĞİÖŞÜa-zçğıöşü\s]+)/i, val: m => `Yaşadığı yer: ${m[1].trim()}` },
232
- { re: /([A-ZÇĞİÖŞÜa-zçğıöşü]{2,})[''](?:da|de|ta|te)\s+(?:yaşıyorum|yaşadığım|oturuyorum|bulunuyorum|kalıyorum)/i, val: m => `Yaşadığı yer: ${m[1]}` },
233
- { re: /(?:seviyorum|severim|sevdiğim|hoşlanırım|beğenirim|like|love)\s+(?:şu|bu)?\s*([a-zA-ZçğıöşüÇĞİÖŞÜ\s]{2,30})/i, val: m => `Sevdiği şey: ${m[1].trim()}` },
234
- { re: /(?:çalışıyorum|işim|mesleğim|çalışırım|I work as)\s+([A-ZÇĞİÖŞÜa-zçğıöşü\s]+)/i, val: m => `Meslek: ${m[1].trim()}` },
235
- { re: /(\d{1,3})\s*(?:yaş[ıi]nday[ıi]m|yaş[ıi]nda)/i, val: m => `Yaş: ${m[1]}` },
227
+ { re: /(?:benim adım|adım|I'm called|my name is)\s+([A-ZÇĞİÖŞÜa-zçğıöşü]+)/i, val: m => `Adı: ${m[1]}` },
228
+ { re: /(?:yaşıyorum|yaşadığım|I live in)\s+([A-ZÇĞİÖŞÜa-zçğıöşü\s]+)/i, val: m => `Yaşadığı yer: ${m[1].trim()}` },
229
+ { re: /(?:seviyorum|severim|sevdiğim|like|love)\s+(?:şu|bu)?\s*([a-zA-ZçğıöşüÇĞİÖŞÜ\s]{2,30})/i, val: m => `Sevdiği şey: ${m[1].trim()}` },
230
+ { re: /(?:çalışıyorum|işim|mesleğim|I work as)\s+([A-ZÇĞİÖŞÜa-zçğıöşü\s]+)/i, val: m => `Meslek: ${m[1].trim()}` },
236
231
  ];
237
232
 
238
233
  for (const p of patterns) {
@@ -251,12 +246,7 @@ function extractFacts(messages, currentFacts) {
251
246
 
252
247
  function apiRequest(providerUrl, providerApiKey, body, stream = false, retries = 3) {
253
248
  return new Promise((resolve, reject) => {
254
- const isMM = isMiniMax(providerUrl);
255
- const endpoint = isMM
256
- ? `${providerUrl.replace(/\/+$/, '')}/v1/text/chatcompletion_v2`
257
- : isGemini(providerUrl)
258
- ? `${providerUrl.replace(/\/+$/, '')}/openai/chat/completions`
259
- : `${providerUrl.replace(/\/+$/, '')}/chat/completions`;
249
+ const endpoint = buildChatEndpoint(providerUrl);
260
250
  const doRequest = (attempt) => {
261
251
  const req = https.request(endpoint, {
262
252
  method: 'POST',
@@ -612,11 +602,9 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
612
602
  const res = await apiRequest(providerUrl, providerApiKey, body, false);
613
603
  const msg = res.choices?.[0]?.message || {};
614
604
  const content = msg.content || '';
615
- if (onChunk !== noopCallback) {
616
- for (const char of content) {
617
- onChunk(char);
618
- await new Promise(r => setTimeout(r, 8));
619
- }
605
+ for (const char of content) {
606
+ onChunk(char);
607
+ await new Promise(r => setTimeout(r, 8));
620
608
  }
621
609
  fullText = content;
622
610
  // Non-stream tool call desteği
@@ -629,13 +617,8 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
629
617
  break;
630
618
  }
631
619
 
632
- // OpenAI uyumlu streaming (veya MiniMax /v1/text/chatcompletion_v2)
633
- // v5.9.5: Gemini /openai/chat/completions — provider-detect.js buildChatEndpoint
634
- const endpoint = isMM
635
- ? `${providerUrl.replace(/\/+$/, '')}/v1/text/chatcompletion_v2`
636
- : isGemini(providerUrl)
637
- ? `${providerUrl.replace(/\/+$/, '')}/openai/chat/completions`
638
- : `${providerUrl.replace(/\/+$/, '')}/chat/completions`;
620
+ // OpenAI uyumlu streaming tek doğruluk kaynağı buildChatEndpoint
621
+ const endpoint = buildChatEndpoint(providerUrl);
639
622
  let result;
640
623
  try {
641
624
  result = await new Promise((resolve, reject) => {
@@ -803,7 +786,7 @@ async function processToolCalls(toolCalls, onToolCall, onAsk) {
803
786
  const name = tc.function?.name || tc.name;
804
787
  const argsStr = tc.function?.arguments || tc.args || '{}';
805
788
  const id = tc.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
806
- let args = {};
789
+ let args;
807
790
  try {
808
791
  args = typeof argsStr === 'string' ? JSON.parse(argsStr) : argsStr;
809
792
  } catch (e) {
@@ -1127,7 +1110,8 @@ async function startRepl(args) {
1127
1110
  console.log(tui.styled(' ' + '─'.repeat(56), { color: tui.PALETTE.border }));
1128
1111
  console.log(tui.C.muted(' Provider: ') + tui.C.brand(providerUrl.replace(/https?:\/\//, '')));
1129
1112
  console.log(tui.C.muted(' Model: ') + tui.C.brand(model));
1130
- console.log(tui.C.muted(' Kullanıcı: ') + tui.C.brand((memory.nickname || cfg.userName) + (memory.nickname ? ` (${cfg.userName})` : '')));
1113
+ const displayUser = memory.nickname || cfg.userName || require('os').userInfo().username || 'Kullanıcı';
1114
+ console.log(tui.C.muted(' Kullanıcı: ') + tui.C.brand(displayUser + (memory.nickname && cfg.userName ? ` (${cfg.userName})` : '')));
1131
1115
  console.log(tui.C.muted(' Bot: ') + tui.C.brand(memory.botName || 'Asistan'));
1132
1116
  if (messages.length > 1) {
1133
1117
  console.log(tui.C.muted(' Oturum: ') + tui.C.amber(`${messages.filter(m => m.role === 'user' || m.role === 'assistant').length} mesaj (resume)`));
@@ -1162,9 +1146,6 @@ async function startRepl(args) {
1162
1146
  let totalInputTokens = 0;
1163
1147
  let totalOutputTokens = 0;
1164
1148
 
1165
- let _pendingCount = 0;
1166
- let _closeRequested = false;
1167
-
1168
1149
  enableBracketedPaste(process.stdout);
1169
1150
 
1170
1151
  const rl = readline.createInterface({
@@ -1173,7 +1154,12 @@ async function startRepl(args) {
1173
1154
  prompt: tui.styled('\n You ', { color: tui.PALETTE.primary, bold: true }),
1174
1155
  terminal: true,
1175
1156
  });
1176
- rl.prompt();
1157
+ // Pipe/script kullanımında EOF, yanıt hâlâ üretilirken gelir —
1158
+ // aktif işlem varken kapanışı bekletmek için sayaç + kapalı-rl koruması
1159
+ let _busy = 0;
1160
+ let _rlClosed = false;
1161
+ const safePrompt = () => { if (!_rlClosed) rl.prompt(); };
1162
+ safePrompt();
1177
1163
 
1178
1164
  const cleanup = async (exitCode = 0) => {
1179
1165
  if (messages.length > 1) {
@@ -1273,10 +1259,18 @@ async function startRepl(args) {
1273
1259
  process.on('SIGTERM', () => cleanup(0));
1274
1260
 
1275
1261
  rl.on('line', async (input) => {
1276
- _pendingCount++;
1262
+ // BOM ve benzeri görünmez karakterleri temizle (PowerShell echo BOM'lu gönderir)
1263
+ const line = restoreNewlines(input).replace(/[\u200B\u200C\u200D\uFEFF]/g, '').trim();
1264
+ if (!line) { safePrompt(); return; }
1265
+ _busy++;
1277
1266
  try {
1278
- const line = restoreNewlines(input).trim();
1279
- if (!line) { rl.prompt(); return; }
1267
+ await handleLine(line);
1268
+ } finally {
1269
+ _busy--;
1270
+ }
1271
+ });
1272
+
1273
+ async function handleLine(line) {
1280
1274
 
1281
1275
  // Çok satırlı paste: output filter'a echo'yu durdurma sinyalini ver
1282
1276
  clearPasteContext();
@@ -1435,7 +1429,7 @@ async function startRepl(args) {
1435
1429
  console.log(chalk.yellow(` Bilinmeyen komut: /${cmd}. /help yazın.`));
1436
1430
  }
1437
1431
  }
1438
- rl.prompt();
1432
+ safePrompt();
1439
1433
  return;
1440
1434
  }
1441
1435
 
@@ -1530,7 +1524,7 @@ async function startRepl(args) {
1530
1524
  messages,
1531
1525
  model,
1532
1526
  // v5.6.12: Callback bos - tam metin 'reply' olarak gelecek (non-stream mode)
1533
- noopCallback,
1527
+ () => {},
1534
1528
  // Tool call callback — Hermes-style per-tool status line
1535
1529
  ((toolEvent) => {
1536
1530
  const name = toolEvent.name;
@@ -1583,21 +1577,17 @@ async function startRepl(args) {
1583
1577
  process.stdout.write('\n');
1584
1578
  console.log(chalk.red(' ❌ ' + err.message));
1585
1579
  }
1586
- rl.prompt();
1587
- } finally {
1588
- _pendingCount--;
1589
- if (_closeRequested && _pendingCount === 0) {
1590
- await cleanup(0);
1591
- }
1592
- }
1593
- });
1580
+ safePrompt();
1581
+ }
1594
1582
 
1595
1583
  rl.on('close', () => {
1596
- if (_pendingCount > 0) {
1597
- _closeRequested = true;
1598
- } else {
1584
+ _rlClosed = true;
1585
+ // EOF (pipe/script): süren yanıt varsa bitmesini bekle, sonra kapan
1586
+ const wait = () => {
1587
+ if (_busy > 0) { setTimeout(wait, 200); return; }
1599
1588
  cleanup(0);
1600
- }
1589
+ };
1590
+ wait();
1601
1591
  });
1602
1592
  }
1603
1593
 
@@ -1,121 +1,121 @@
1
- /**
2
- * mem0-memory.js — Mem0 memory provider
3
- *
4
- * Uses the mem0ai npm package (platform API) or local OSS Memory class.
5
- * Requires MEM0_API_KEY env var or config.mem0.apiKey.
6
- * npm install mem0ai
7
- */
8
-
9
- const { MemoryProvider, registerProvider } = require('../utils/memory-provider');
10
-
11
- class Mem0MemoryProvider extends MemoryProvider {
12
- constructor(config = {}) {
13
- super(config);
14
- this.name = 'mem0';
15
- this._client = null;
16
- this._mode = 'platform'; // or 'oss'
17
- }
18
-
19
- _getConfig() {
20
- return this.config.mem0 || this.config.Mem0 || {};
21
- }
22
-
23
- _apiKey() {
24
- return process.env.MEM0_API_KEY || this._getConfig().apiKey || '';
25
- }
26
-
27
- async _ensureClient() {
28
- if (this._client) return;
29
- try {
30
- const apiKey = this._apiKey();
31
- if (apiKey) {
32
- const { MemoryClient } = await import('mem0ai');
33
- this._client = new MemoryClient({ apiKey });
34
- this._mode = 'platform';
35
- } else {
36
- const { Memory } = await import('mem0ai/oss');
37
- this._client = new Memory(this._getConfig().oss || {});
38
- this._mode = 'oss';
39
- }
40
- } catch (e) {
41
- throw new Error('mem0ai paketi yuklu degil. npm install mem0ai');
42
- }
43
- }
44
-
45
- async add(userId, content, metadata = {}) {
46
- await this._ensureClient();
47
- const uid = userId || 'default';
48
- if (this._mode === 'platform') {
49
- const messages = [{ role: 'user', content }];
50
- await this._client.add(messages, { userId: uid, metadata });
51
- } else {
52
- const messages = [
53
- { role: 'user', content },
54
- { role: 'assistant', content: 'Bilgi kaydedildi.' },
55
- ];
56
- await this._client.add(messages, { userId: uid, metadata });
57
- }
58
- return { success: true, id: content, message: 'Mem0 memory added', provider: 'mem0' };
59
- }
60
-
61
- async search(query, options = {}) {
62
- await this._ensureClient();
63
- const uid = options.userId;
64
- const filters = uid ? { userId: uid } : {};
65
- if (options.limit) filters.limit = options.limit;
66
- try {
67
- const results = await this._client.search(query, { filters });
68
- return { success: true, results: (results.results || results || []).map(r => ({
69
- id: r.id || r.memory,
70
- content: r.memory || r.content || '',
71
- score: r.score || 0,
72
- metadata: r.metadata || {},
73
- })) };
74
- } catch (e) {
75
- return { success: false, error: e.message, results: [] };
76
- }
77
- }
78
-
79
- async list(userId) {
80
- await this._ensureClient();
81
- const uid = userId || 'default';
82
- try {
83
- const memories = await this._client.getAll({ userId: uid });
84
- return {
85
- success: true,
86
- memories: (memories || []).map(r => ({
87
- id: r.id || r.memory,
88
- content: r.memory || r.content || '',
89
- score: r.score || 0,
90
- metadata: r.metadata || {},
91
- })),
92
- };
93
- } catch (e) {
94
- return { success: false, error: e.message, memories: [] };
95
- }
96
- }
97
-
98
- async remove(id) {
99
- await this._ensureClient();
100
- try {
101
- await this._client.delete(id);
102
- return { success: true, message: 'Mem0 memory removed' };
103
- } catch (e) {
104
- return { success: false, error: e.message };
105
- }
106
- }
107
-
108
- async clear(userId) {
109
- await this._ensureClient();
110
- const uid = userId || 'default';
111
- try {
112
- await this._client.deleteAll({ userId: uid });
113
- return { success: true, message: `Mem0 memory cleared for ${uid}` };
114
- } catch (e) {
115
- return { success: false, error: e.message };
116
- }
117
- }
118
- }
119
-
120
- registerProvider('mem0', Mem0MemoryProvider);
121
- module.exports = Mem0MemoryProvider;
1
+ /**
2
+ * mem0-memory.js — Mem0 memory provider
3
+ *
4
+ * Uses the mem0ai npm package (platform API) or local OSS Memory class.
5
+ * Requires MEM0_API_KEY env var or config.mem0.apiKey.
6
+ * npm install mem0ai
7
+ */
8
+
9
+ const { MemoryProvider, registerProvider } = require('../utils/memory-provider');
10
+
11
+ class Mem0MemoryProvider extends MemoryProvider {
12
+ constructor(config = {}) {
13
+ super(config);
14
+ this.name = 'mem0';
15
+ this._client = null;
16
+ this._mode = 'platform'; // or 'oss'
17
+ }
18
+
19
+ _getConfig() {
20
+ return this.config.mem0 || this.config.Mem0 || {};
21
+ }
22
+
23
+ _apiKey() {
24
+ return process.env.MEM0_API_KEY || this._getConfig().apiKey || '';
25
+ }
26
+
27
+ async _ensureClient() {
28
+ if (this._client) return;
29
+ try {
30
+ const apiKey = this._apiKey();
31
+ if (apiKey) {
32
+ const { MemoryClient } = await import('mem0ai');
33
+ this._client = new MemoryClient({ apiKey });
34
+ this._mode = 'platform';
35
+ } else {
36
+ const { Memory } = await import('mem0ai/oss');
37
+ this._client = new Memory(this._getConfig().oss || {});
38
+ this._mode = 'oss';
39
+ }
40
+ } catch (e) {
41
+ throw new Error('mem0ai paketi yuklu degil. npm install mem0ai', { cause: e });
42
+ }
43
+ }
44
+
45
+ async add(userId, content, metadata = {}) {
46
+ await this._ensureClient();
47
+ const uid = userId || 'default';
48
+ if (this._mode === 'platform') {
49
+ const messages = [{ role: 'user', content }];
50
+ await this._client.add(messages, { userId: uid, metadata });
51
+ } else {
52
+ const messages = [
53
+ { role: 'user', content },
54
+ { role: 'assistant', content: 'Bilgi kaydedildi.' },
55
+ ];
56
+ await this._client.add(messages, { userId: uid, metadata });
57
+ }
58
+ return { success: true, id: content, message: 'Mem0 memory added', provider: 'mem0' };
59
+ }
60
+
61
+ async search(query, options = {}) {
62
+ await this._ensureClient();
63
+ const uid = options.userId;
64
+ const filters = uid ? { userId: uid } : {};
65
+ if (options.limit) filters.limit = options.limit;
66
+ try {
67
+ const results = await this._client.search(query, { filters });
68
+ return { success: true, results: (results.results || results || []).map(r => ({
69
+ id: r.id || r.memory,
70
+ content: r.memory || r.content || '',
71
+ score: r.score || 0,
72
+ metadata: r.metadata || {},
73
+ })) };
74
+ } catch (e) {
75
+ return { success: false, error: e.message, results: [] };
76
+ }
77
+ }
78
+
79
+ async list(userId) {
80
+ await this._ensureClient();
81
+ const uid = userId || 'default';
82
+ try {
83
+ const memories = await this._client.getAll({ userId: uid });
84
+ return {
85
+ success: true,
86
+ memories: (memories || []).map(r => ({
87
+ id: r.id || r.memory,
88
+ content: r.memory || r.content || '',
89
+ score: r.score || 0,
90
+ metadata: r.metadata || {},
91
+ })),
92
+ };
93
+ } catch (e) {
94
+ return { success: false, error: e.message, memories: [] };
95
+ }
96
+ }
97
+
98
+ async remove(id) {
99
+ await this._ensureClient();
100
+ try {
101
+ await this._client.delete(id);
102
+ return { success: true, message: 'Mem0 memory removed' };
103
+ } catch (e) {
104
+ return { success: false, error: e.message };
105
+ }
106
+ }
107
+
108
+ async clear(userId) {
109
+ await this._ensureClient();
110
+ const uid = userId || 'default';
111
+ try {
112
+ await this._client.deleteAll({ userId: uid });
113
+ return { success: true, message: `Mem0 memory cleared for ${uid}` };
114
+ } catch (e) {
115
+ return { success: false, error: e.message };
116
+ }
117
+ }
118
+ }
119
+
120
+ registerProvider('mem0', Mem0MemoryProvider);
121
+ module.exports = Mem0MemoryProvider;