natureco-cli 5.9.1 → 5.9.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.9.1",
3
+ "version": "5.9.3",
4
4
  "description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
5
5
  "bin": {
6
6
  "natureco": "bin/natureco.js"
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: youtube-ac
3
+ description: YouTube videosunu mevcut tarayıcıda yeni sekmede veya yeni tarayıcıda açar
4
+ trigger: "youtube aç, youtube videosu aç, şarkı aç, müzik aç, video aç"
5
+ ---
6
+
7
+ # YouTube Aç Skill'i
8
+
9
+ ## Description
10
+ YouTube videosunu tarayıcıda açar - eğer bir tarayıcı açıksa yeni sekmede, yoksa yeni pencerede açar.
11
+
12
+ ## Parameters
13
+ - `query`: YouTube video adı veya arama terimi
14
+ - `url`: Doğrudan YouTube URL'si (opsiyonel)
15
+
16
+ ## Actions
17
+
18
+ ### Step 1: URL veya Arama Terimi Al
19
+ Eğer kullanıcı doğrudan URL vermişse Step 2'ye geç.
20
+ Eğer arama terimi verdiyse, YouTube arama sayfasını aç.
21
+
22
+ ### Step 2: Tarayıcı Kontrolü
23
+ Hangi tarayıcıların açık olduğunu kontrol et:
24
+ ```bash
25
+ pgrep -x "Google Chrome"
26
+ pgrep -x "Safari"
27
+ pgrep -x "Firefox"
28
+ ```
29
+
30
+ ### Step 3: Aç
31
+ - **Chrome açıksa:**
32
+ ```bash
33
+ open -a "Google Chrome" "YOUTUBE_URL"
34
+ ```
35
+ - **Safari açıksa:**
36
+ ```bash
37
+ open -a Safari "YOUTUBE_URL"
38
+ ```
39
+ - **Firefox açıksa:**
40
+ ```bash
41
+ open -a Firefox "YOUTUBE_URL"
42
+ ```
43
+ - **Hiçbiri açık değilse:**
44
+ ```bash
45
+ open "YOUTUBE_URL"
46
+ ```
47
+
48
+ ## Notes
49
+ - `open` komutu açık uygulamaya URL verildiğinde otomatik yeni sekmede açar.
50
+ - Öncelik sırası: Chrome > Safari > Firefox
51
+ - Arama yapılacaksa "YouTube [şarkı adı]" formatında ara
52
+ - Tool olarak `youtube_ac` kullanılabilir
@@ -75,7 +75,8 @@ async function chat(botName, options = {}) {
75
75
  // Önceki v2.23 davranışı: ASCII art, bot seçimi, inquirer prompt, vb.
76
76
  // Bu refactor, eski tüm komutları (session, memory, hooks, custom commands) korur
77
77
  // ama provider URL (api.minimax.io, api.groq.com) üzerinden direkt LLM'e bağlanır
78
- if (config.providerUrl && !config.providerUrl.includes('natureco.me')) {
78
+ // Provider ayarlı değilse veya natureco.me değilse REPL'e yönlendir
79
+ if (!config.providerUrl || !config.providerUrl.includes('natureco.me')) {
79
80
  // Resume parametresi REPL'e geçir
80
81
  const replArgs = [];
81
82
  if (options.resume === true || options.resume) {
@@ -310,6 +310,8 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
310
310
 
311
311
  while (iterations < MAX_TOOL_ITERATIONS) {
312
312
  iterations++;
313
+ // v5.7.18: Preflight compress before each iteration to prevent context bloat
314
+ currentMessages = preflightCompress(currentMessages);
313
315
  const shouldStream = !isMM; // MiniMax streaming endpoint doesn't support tool_calls
314
316
  const body = {
315
317
  model,
@@ -414,7 +416,7 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
414
416
  tool_calls: finalized,
415
417
  });
416
418
  // Her tool call'ı çalıştır, sonuçları tool mesajı olarak ekle
417
- const toolResults = await processToolCalls(result.toolCalls, onToolCall);
419
+ const toolResults = await processToolCalls(finalized, onToolCall);
418
420
  currentMessages.push(...toolResults);
419
421
  // Devam — model sonuçları görsün, cevap versin
420
422
  continue;
@@ -0,0 +1,187 @@
1
+ const { spawn, execSync } = require("child_process");
2
+ const os = require("os");
3
+
4
+ const IS_MAC = os.platform() === "darwin";
5
+
6
+ const PLATFORMS = {
7
+ youtube: {
8
+ match: ["youtube", "yt", "youtu.be"],
9
+ url: (id) => id.match(/^https?:\/\//) ? id
10
+ : id.startsWith("@") ? `https://www.youtube.com/${id}`
11
+ : id.startsWith("UC") ? `https://www.youtube.com/channel/${id}`
12
+ : `https://www.youtube.com/@${id}`,
13
+ },
14
+ twitter: {
15
+ match: ["twitter", "x.com", "x "],
16
+ url: (id) => id.startsWith("http") ? id : `https://x.com/${id.replace(/^@/, "")}`,
17
+ },
18
+ instagram: {
19
+ match: ["instagram", "ig", "insta"],
20
+ url: (id) => id.startsWith("http") ? id : `https://www.instagram.com/${id.replace(/^@/, "")}/`,
21
+ },
22
+ tiktok: {
23
+ match: ["tiktok", "tt"],
24
+ url: (id) => id.startsWith("http") ? id : `https://www.tiktok.com/@${id.replace(/^@/, "")}`,
25
+ },
26
+ linkedin: {
27
+ match: ["linkedin", "linked in", "in"],
28
+ url: (id) => id.startsWith("http") ? id : `https://www.linkedin.com/in/${id.replace(/^@/, "")}`,
29
+ },
30
+ github: {
31
+ match: ["github", "gh", "git"],
32
+ url: (id) => id.startsWith("http") ? id : `https://github.com/${id.replace(/^@/, "")}`,
33
+ },
34
+ reddit: {
35
+ match: ["reddit", "r/"],
36
+ url: (id) => id.startsWith("http") ? id
37
+ : id.startsWith("r/") ? `https://www.reddit.com/${id}`
38
+ : `https://www.reddit.com/user/${id.replace(/^u\//, "")}`,
39
+ },
40
+ facebook: {
41
+ match: ["facebook", "fb"],
42
+ url: (id) => id.startsWith("http") ? id : `https://www.facebook.com/${id.replace(/^@/, "")}`,
43
+ },
44
+ twitch: {
45
+ match: ["twitch", "tv"],
46
+ url: (id) => id.startsWith("http") ? id : `https://www.twitch.tv/${id.replace(/^@/, "")}`,
47
+ },
48
+ medium: {
49
+ match: ["medium"],
50
+ url: (id) => id.startsWith("http") ? id : `https://medium.com/@${id.replace(/^@/, "")}`,
51
+ },
52
+ spotify: {
53
+ match: ["spotify", "spt"],
54
+ url: (id) => id.startsWith("http") ? id : `https://open.spotify.com/search/${encodeURIComponent(id)}`,
55
+ },
56
+ };
57
+
58
+ function getOpenBrowser() {
59
+ const browsers = [
60
+ { name: "Google Chrome", cmd: "Google Chrome" },
61
+ { name: "Safari", cmd: "Safari" },
62
+ { name: "Firefox", cmd: "Firefox" },
63
+ ];
64
+ for (const browser of browsers) {
65
+ try {
66
+ const result = execSync(`pgrep -x "${browser.name}"`, { encoding: "utf8" });
67
+ if (result.trim()) return browser.cmd;
68
+ } catch (e) {}
69
+ }
70
+ return null;
71
+ }
72
+
73
+ function detectPlatform(input) {
74
+ const lower = input.toLowerCase().trim();
75
+
76
+ // Already a URL
77
+ if (lower.startsWith("http://") || lower.startsWith("https://")) {
78
+ for (const [name, p] of Object.entries(PLATFORMS)) {
79
+ if (p.match.some(m => lower.includes(m))) {
80
+ return { platform: name, id: input, url: input };
81
+ }
82
+ }
83
+ return { platform: "web", id: input, url: input };
84
+ }
85
+
86
+ // "platform:username" pattern (e.g. "yt:gencay", "gh:gencay")
87
+ const colonMatch = lower.match(/^(\w+):(.+)$/);
88
+ if (colonMatch) {
89
+ const [, platformKey, username] = colonMatch;
90
+ for (const [name, p] of Object.entries(PLATFORMS)) {
91
+ if (p.match.some(m => platformKey === m || platformKey.startsWith(m))) {
92
+ return { platform: name, id: username, url: p.url(username) };
93
+ }
94
+ }
95
+ }
96
+
97
+ // Platform prefix match (e.g. "github gencay", "twitter @elon")
98
+ for (const [name, p] of Object.entries(PLATFORMS)) {
99
+ for (const m of p.match) {
100
+ if (lower.startsWith(m) && lower.length > m.length) {
101
+ const username = input.slice(m.length).trim().replace(/^@?/, "");
102
+ return { platform: name, id: username, url: p.url(username) };
103
+ }
104
+ }
105
+ }
106
+
107
+ // Auto-detect: if input has an @ prefix or looks like a handle, try common platforms
108
+ if (input.startsWith("@")) {
109
+ const username = input.replace(/^@/, "");
110
+ return {
111
+ platform: "auto",
112
+ id: username,
113
+ url: `https://www.google.com/search?q=${encodeURIComponent(input)}`,
114
+ note: "Hesap adı algılandı. Hangi platform olduğunu belirt (yt:, gh:, twitter: vb).",
115
+ };
116
+ }
117
+
118
+ // Fallback: Google search
119
+ return {
120
+ platform: "search",
121
+ id: input,
122
+ url: `https://www.google.com/search?q=${encodeURIComponent(input)}`,
123
+ note: "Platform algılanamadı. Google'da aratılıyor. Kullanım: 'twitter @kullanici', 'github repo', 'yt:kanaladi'",
124
+ };
125
+ }
126
+
127
+ async function socialOpen(params) {
128
+ if (!IS_MAC) return { success: false, error: "Henüz sadece macOS destekleniyor" };
129
+
130
+ const { query, platform, username } = params;
131
+
132
+ if (!query && !platform && !username) {
133
+ return { success: false, error: "query veya platform+username gerekli" };
134
+ }
135
+
136
+ let url, platformName, note;
137
+ if (platform && username) {
138
+ const p = PLATFORMS[platform.toLowerCase()];
139
+ if (!p) return { success: false, error: `Bilinmeyen platform: ${platform}. Desteklenenler: ${Object.keys(PLATFORMS).join(", ")}` };
140
+ url = p.url(username);
141
+ platformName = platform;
142
+ } else {
143
+ const detected = detectPlatform(query);
144
+ url = detected.url;
145
+ platformName = detected.platform;
146
+ note = detected.note;
147
+ }
148
+
149
+ const browser = getOpenBrowser();
150
+ return new Promise((resolve) => {
151
+ const args = browser ? ["-a", browser, url] : [url];
152
+ const proc = spawn("open", args);
153
+ proc.on("close", (code) => {
154
+ if (code === 0) {
155
+ resolve({
156
+ success: true,
157
+ message: browser
158
+ ? `${browser}'da yeni sekmede açıldı`
159
+ : "Yeni tarayıcı penceresinde açıldı",
160
+ platform: platformName,
161
+ url,
162
+ browser: browser || "new",
163
+ ...(note ? { note } : {}),
164
+ });
165
+ } else {
166
+ resolve({ success: false, error: "Açma hatası" });
167
+ }
168
+ });
169
+ proc.on("error", (e) => resolve({ success: false, error: e.message }));
170
+ });
171
+ }
172
+
173
+ module.exports = {
174
+ name: "social_open",
175
+ description: "Sosyal medya hesabını veya sayfasını mevcut tarayıcıda yeni sekmede açar. Destek: youtube, twitter/x, instagram, tiktok, linkedin, github, reddit, facebook, twitch, medium, spotify. Kullanım: 'twitter @kullanici', 'github repo', 'yt:kanaladi', 'instagram profili'",
176
+ inputSchema: {
177
+ type: "object",
178
+ properties: {
179
+ query: { type: "string", description: "Platform + kullanici adi veya arama terimi (örn: 'twitter @elon', 'github gencay', 'yt:kanaladi')" },
180
+ platform: { type: "string", description: "Platform adi (youtube, twitter, instagram, tiktok, linkedin, github, reddit, facebook, twitch, medium, spotify)" },
181
+ username: { type: "string", description: "Kullanici adi veya kanal adi (platform ile birlikte kullanilir)" },
182
+ },
183
+ },
184
+ async execute(params) {
185
+ return await socialOpen(params);
186
+ },
187
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * youtube_ac - YouTube videosunu mevcut tarayıcıda yeni sekmede açar
3
+ */
4
+
5
+ const { spawn, execSync } = require("child_process");
6
+ const os = require("os");
7
+
8
+ const IS_MAC = os.platform() === "darwin";
9
+
10
+ function getOpenBrowser() {
11
+ const browsers = [
12
+ { name: "Google Chrome", cmd: "Google Chrome" },
13
+ { name: "Safari", cmd: "Safari" },
14
+ { name: "Firefox", cmd: "Firefox" }
15
+ ];
16
+
17
+ for (const browser of browsers) {
18
+ try {
19
+ const result = execSync(`pgrep -x "${browser.name}"`, { encoding: "utf8" });
20
+ if (result.trim()) {
21
+ return browser.cmd;
22
+ }
23
+ } catch (e) {
24
+ // Browser açık değil
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+
30
+ function isUrl(str) {
31
+ return str.startsWith("http://") || str.startsWith("https://");
32
+ }
33
+
34
+ function isYoutubeUrl(str) {
35
+ return str.includes("youtube.com") || str.includes("youtu.be");
36
+ }
37
+
38
+ async function youtubeAc(params) {
39
+ if (!IS_MAC) return { success: false, error: "macOS'e özgü" };
40
+
41
+ const { query, url } = params;
42
+
43
+ if (!query && !url) {
44
+ return { success: false, error: "query veya url gerekli" };
45
+ }
46
+
47
+ let youtubeUrl = url || query;
48
+
49
+ // URL değilse YouTube arama sayfasını aç
50
+ if (!isUrl(youtubeUrl)) {
51
+ // Boşlukları + ile değiştir ve YouTube arama URL'i oluştur
52
+ const searchTerm = encodeURIComponent(youtubeUrl);
53
+ youtubeUrl = `https://www.youtube.com/results?search_query=${searchTerm}`;
54
+ }
55
+
56
+ // YouTube URL değilse hata ver
57
+ if (!isYoutubeUrl(youtubeUrl) && !youtubeUrl.includes("youtube.com")) {
58
+ return { success: false, error: "Lütfen geçerli bir YouTube URL'si veya video adı girin" };
59
+ }
60
+
61
+ // Tarayıcı kontrolü
62
+ const browser = getOpenBrowser();
63
+
64
+ return new Promise((resolve) => {
65
+ const args = browser
66
+ ? ["-a", browser, youtubeUrl]
67
+ : [youtubeUrl];
68
+
69
+ const proc = spawn("open", args);
70
+ proc.on("close", code => {
71
+ if (code === 0) {
72
+ resolve({
73
+ success: true,
74
+ message: browser
75
+ ? `${browser}'da yeni sekmede açıldı`
76
+ : "Yeni tarayıcı penceresinde açıldı",
77
+ url: youtubeUrl,
78
+ browser: browser || "new"
79
+ });
80
+ } else {
81
+ resolve({ success: false, error: "Açma hatası" });
82
+ }
83
+ });
84
+ proc.on("error", e => resolve({ success: false, error: e.message }));
85
+ });
86
+ }
87
+
88
+ module.exports = {
89
+ name: "youtube_ac",
90
+ description: "YouTube videosunu mevcut tarayıcıda yeni sekmede açar (Chrome > Safari > Firefox > yeni pencere)",
91
+ inputSchema: {
92
+ type: "object",
93
+ properties: {
94
+ query: { type: "string", description: "Aranacak şarkı/video adı (YouTube arama sayfasını açar)" },
95
+ url: { type: "string", description: "Doğrudan YouTube URL'si (opsiyonel)" },
96
+ },
97
+ },
98
+ async execute(params) {
99
+ return await youtubeAc(params);
100
+ },
101
+ };
package/src/utils/api.js CHANGED
@@ -1084,18 +1084,20 @@ async function streamOpenAICompletion(providerConfig, messages, tools) {
1084
1084
  }
1085
1085
  }
1086
1086
 
1087
- if (hasToolCalls) {
1087
+ if (hasToolCalls) {
1088
1088
  process.stdout.write('\n');
1089
1089
  return {
1090
1090
  type: 'tool_calls',
1091
1091
  message: {
1092
1092
  role: 'assistant',
1093
1093
  content: fullText || null,
1094
- tool_calls: toolCalls.map(tc => ({
1095
- id: tc.id,
1096
- type: tc.type,
1097
- function: { name: tc.function.name, arguments: tc.function.arguments }
1098
- }))
1094
+ tool_calls: toolCalls
1095
+ .filter(tc => tc && tc.function && tc.function.name)
1096
+ .map(tc => ({
1097
+ id: tc.id || `call_${Date.now()}_${tc.index}`,
1098
+ type: tc.type || 'function',
1099
+ function: { name: tc.function.name, arguments: tc.function.arguments || '' }
1100
+ }))
1099
1101
  }
1100
1102
  };
1101
1103
  }