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 +1 -1
- package/skills/youtube-ac/SKILL.md +52 -0
- package/src/commands/chat.js +2 -1
- package/src/commands/repl.js +3 -1
- package/src/tools/social_open.js +187 -0
- package/src/tools/youtube_ac.js +101 -0
- package/src/utils/api.js +8 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.9.
|
|
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
|
package/src/commands/chat.js
CHANGED
|
@@ -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
|
-
|
|
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) {
|
package/src/commands/repl.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
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
|
}
|