natureco-cli 4.8.4 → 4.9.1

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": "4.8.4",
3
+ "version": "4.9.1",
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,94 @@
1
+ /**
2
+ * calendar_add - macOS Calendar'a etkinlik ekle (v4.9.1)
3
+ *
4
+ * Parton'un OS-level kontrol vizyonu için.
5
+ * "Yarin saat 14:00 toplantim var" -> Takvime ekler.
6
+ */
7
+
8
+ const { spawn } = require("child_process");
9
+ const os = require("os");
10
+
11
+ const IS_MAC = os.platform() === "darwin";
12
+
13
+ function runAppleScript(script) {
14
+ return new Promise((resolve, reject) => {
15
+ const proc = spawn("osascript", ["-e", script]);
16
+ let stdout = "";
17
+ let stderr = "";
18
+ proc.stdout.on("data", d => stdout += d);
19
+ proc.stderr.on("data", d => stderr += d);
20
+ proc.on("close", code => {
21
+ if (code === 0) resolve(stdout.trim());
22
+ else reject(new Error(stderr.trim() || `osascript exit ${code}`));
23
+ });
24
+ });
25
+ }
26
+
27
+ async function calendarAdd(params) {
28
+ if (!IS_MAC) return { success: false, error: "Calendar sadece macOS'ta desteklenir" };
29
+ const { title, startDate = "now", duration = 60, calendar = null, notes = "", location = "" } = params;
30
+ if (!title) return { success: false, error: "title gerekli" };
31
+
32
+ let startScript;
33
+ if (startDate === "now") startScript = "current date";
34
+ else if (typeof startDate === "string" && startDate.startsWith("+")) {
35
+ const m = startDate.match(/\+(\d+)\s*(hour|day|minute)/i);
36
+ if (m) startScript = `(current date) + ${m[1]} * ${m[2].toLowerCase()}s`;
37
+ else startScript = "current date";
38
+ } else {
39
+ startScript = `date "${startDate}"`;
40
+ }
41
+
42
+ const calScript = calendar ? `calendar "${calendar}"` : `first calendar whose writable is true`;
43
+
44
+ const script = `
45
+ tell application "Calendar"
46
+ set targetCal to ${calScript}
47
+ set startDate to ${startScript}
48
+ set endDate to startDate + (${duration} * minutes)
49
+ set newEvent to make new event at end of events of targetCal with properties {summary:"${title.replace(/"/g, "'")}", start date:startDate, end date:endDate${location ? `, location:"${location.replace(/"/g, "'")}"` : ""}${notes ? `, description:"${notes.replace(/"/g, "'")}"` : ""}}
50
+ save
51
+ return id of newEvent
52
+ end tell
53
+ `;
54
+
55
+ try {
56
+ const eventId = await runAppleScript(script);
57
+ return {
58
+ success: true,
59
+ eventId,
60
+ title,
61
+ startDate,
62
+ duration,
63
+ message: `Takvime eklendi: "${title}" (${startDate} + ${duration}dk)`,
64
+ };
65
+ } catch (e) {
66
+ if (e.message.includes("-1728") || e.message.includes("not authorized")) {
67
+ return {
68
+ success: false,
69
+ error: "Calendar erisim izni yok.\n\nIzin vermek icin:\n1. System Preferences -> Security & Privacy -> Privacy -> Automation\n2. natureco (veya Terminal) -> Calendar -> ON\n3. Tekrar dene",
70
+ };
71
+ }
72
+ return { success: false, error: e.message };
73
+ }
74
+ }
75
+
76
+ module.exports = {
77
+ name: "calendar_add",
78
+ description: "macOS Calendar'a etkinlik ekle. 'Yarin 14:00 toplanti' gibi.",
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ title: { type: "string", description: "Etkinlik basligi" },
83
+ startDate: { type: "string", description: 'Baslangic: "now", "+1 hour", "+2 days", veya ISO "2026-06-23 14:00"' },
84
+ duration: { type: "number", description: "Dakika (default 60)" },
85
+ calendar: { type: "string", description: "Takvim adi (default: ilk yazilabilir)" },
86
+ notes: { type: "string", description: "Notlar" },
87
+ location: { type: "string", description: "Konum" },
88
+ },
89
+ required: ["title"],
90
+ },
91
+ async execute(params) {
92
+ return await calendarAdd(params);
93
+ },
94
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Code Execution — Python/Node kodu sandbox'ta çalıştır (v4.9.0)
3
+ *
4
+ * Hermes'ın execute_code'una benzer.
5
+ * Kullanıcı "Python ile X yap" derse çalıştırır.
6
+ */
7
+
8
+ const { spawn } = require('child_process');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const os = require('os');
12
+
13
+ /**
14
+ * Kod çalıştır, çıktıyı ve hataları döndür.
15
+ * @param code - Çalıştırılacak kod
16
+ * @param language - 'python', 'node', 'bash' (default: otomatik tespit)
17
+ * @param timeoutMs - Timeout (default 30s)
18
+ */
19
+ async function runCode({ code, language = 'auto', timeoutMs = 30000, cwd = null }) {
20
+ if (!code) return { success: false, error: 'code gerekli' };
21
+
22
+ // Dil tespiti
23
+ let cmd, args;
24
+ if (language === 'auto') {
25
+ if (/^import |^from |def |print\(/m.test(code) || code.includes('python')) {
26
+ language = 'python';
27
+ } else if (/require\(|^const |^let |^var |console\./m.test(code)) {
28
+ language = 'node';
29
+ } else if (/^(ls|cat|echo|grep|find|cd|mkdir)/m.test(code)) {
30
+ language = 'bash';
31
+ } else {
32
+ language = 'bash';
33
+ }
34
+ }
35
+
36
+ if (language === 'python') { cmd = 'python3'; args = ['-c', code]; }
37
+ else if (language === 'node') { cmd = 'node'; args = ['-e', code]; }
38
+ else if (language === 'bash' || language === 'shell') { cmd = 'bash'; args = ['-c', code]; }
39
+ else return { success: false, error: `Desteklenmeyen dil: ${language}` };
40
+
41
+ return new Promise((resolve) => {
42
+ const proc = spawn(cmd, args, {
43
+ cwd: cwd || os.homedir(),
44
+ timeout: timeoutMs,
45
+ env: { ...process.env, FORCE_COLOR: '0' },
46
+ });
47
+
48
+ let stdout = '';
49
+ let stderr = '';
50
+ proc.stdout.on('data', d => stdout += d.toString());
51
+ proc.stderr.on('data', d => stderr += d.toString());
52
+
53
+ proc.on('close', (code) => {
54
+ const truncated = (s) => s.length > 8000 ? s.slice(0, 8000) + '\n... (kesildi, ' + (s.length - 8000) + ' karakter daha)' : s;
55
+ resolve({
56
+ success: code === 0,
57
+ language,
58
+ exitCode: code,
59
+ stdout: truncated(stdout).trim(),
60
+ stderr: truncated(stderr).trim(),
61
+ output: truncated(stdout + (stderr ? '\n[STDERR]: ' + stderr : '')).trim(),
62
+ });
63
+ });
64
+ proc.on('error', (e) => {
65
+ resolve({ success: false, error: e.message, language });
66
+ });
67
+ });
68
+ }
69
+
70
+ module.exports = {
71
+ name: 'code_execution',
72
+ description: 'Python/Node/Bash kodu sandbox\'ta çalıştır. Çıktıyı ve hataları döndürür.',
73
+ inputSchema: {
74
+ type: 'object',
75
+ properties: {
76
+ code: { type: 'string', description: 'Çalıştırılacak kod' },
77
+ language: { type: 'string', description: 'Dil: python, node, bash (default: otomatik tespit)', enum: ['auto', 'python', 'node', 'bash'] },
78
+ timeoutMs: { type: 'number', description: 'Timeout ms (default 30000)' },
79
+ cwd: { type: 'string', description: 'Çalışma dizini (default: home)' },
80
+ },
81
+ required: ['code'],
82
+ },
83
+ async execute(params) {
84
+ return await runCode(params);
85
+ },
86
+ };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * cron_create - Zamanlanmis gorev (v4.9.0)
3
+ *
4
+ * Hermes cronjob'una benzer. "Her gun 09:00'da calistir" gibi.
5
+ * NatureCo CLI mevcut cron sistemine yazar (cron.js zaten var).
6
+ */
7
+
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+ const os = require("os");
11
+ const { execSync } = require("child_process");
12
+
13
+ const CRON_FILE = path.join(os.homedir(), ".natureco", "crons.json");
14
+
15
+ function loadCrons() {
16
+ try {
17
+ if (!fs.existsSync(CRON_FILE)) return [];
18
+ return JSON.parse(fs.readFileSync(CRON_FILE, "utf8"));
19
+ } catch { return []; }
20
+ }
21
+
22
+ function saveCrons(crons) {
23
+ fs.mkdirSync(path.dirname(CRON_FILE), { recursive: true });
24
+ fs.writeFileSync(CRON_FILE, JSON.stringify(crons, null, 2), "utf8");
25
+ }
26
+
27
+ function genId() {
28
+ return "cron-" + Date.now().toString(36);
29
+ }
30
+
31
+ // Yaygin zamanlama ifadeleri
32
+ const SCHEDULE_PRESETS = {
33
+ "every minute": "* * * * *",
34
+ "every 5 minutes": "*/5 * * * *",
35
+ "every 15 minutes": "*/15 * * * *",
36
+ "every hour": "0 * * * *",
37
+ "every day 9am": "0 9 * * *",
38
+ "every day midnight": "0 0 * * *",
39
+ "every monday 9am": "0 9 * * 1",
40
+ "every sunday": "0 9 * * 0",
41
+ };
42
+
43
+ async function createCron({ name, schedule, command, description = "" }) {
44
+ if (!name) return { success: false, error: "name gerekli" };
45
+ if (!schedule) return { success: false, error: "schedule gerekli (cron expression veya preset)" };
46
+ if (!command) return { success: false, error: "command gerekli (calistirilacak komut)" };
47
+
48
+ // Preset kontrolu
49
+ const resolvedSchedule = SCHEDULE_PRESETS[schedule.toLowerCase()] || schedule;
50
+
51
+ // Cron expression dogrulama (basit)
52
+ if (!/^[\d*/\s,-]+$/.test(resolvedSchedule)) {
53
+ return { success: false, error: `Gecersiz cron expression: ${resolvedSchedule}` };
54
+ }
55
+
56
+ const crons = loadCrons();
57
+ const newCron = {
58
+ id: genId(),
59
+ name,
60
+ schedule: resolvedSchedule,
61
+ command,
62
+ description,
63
+ enabled: true,
64
+ createdAt: new Date().toISOString(),
65
+ };
66
+ crons.push(newCron);
67
+ saveCrons(crons);
68
+
69
+ // İstege bagli: gercek crontab'a da ekle (sistem cron)
70
+ // Bu tehlikeli olabilir, sadece bilgi veriyoruz
71
+ let systemCrontabInstalled = false;
72
+ try {
73
+ const crontabLine = `${resolvedSchedule} ${command} # natureco:${name}`;
74
+ const current = execSync("crontab -l 2>/dev/null || echo ''", { encoding: "utf8" });
75
+ if (!current.includes(`# natureco:${name}`)) {
76
+ // Kullaniciya soru sormadan eklemiyoruz - sadece komutu gosteriyoruz
77
+ systemCrontabInstalled = false;
78
+ }
79
+ } catch {}
80
+
81
+ return {
82
+ success: true,
83
+ cron: newCron,
84
+ message: `Cron olusturuldu: ${name} (${resolvedSchedule})`,
85
+ hint: systemCrontabInstalled ? undefined : `Sistem crontab'a eklemek icin: crontab -e ve ekleyin: ${resolvedSchedule} ${command}`,
86
+ };
87
+ }
88
+
89
+ module.exports = {
90
+ name: "cron_create",
91
+ description: "Zamanlanmis gorev olustur. Schedule: cron expression veya preset ('every day 9am', 'every hour').",
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ name: { type: "string", description: "Gorev adi" },
96
+ schedule: { type: "string", description: "Cron expression (ornek: '0 9 * * *') veya preset" },
97
+ command: { type: "string", description: "Calistirilacak komut" },
98
+ description: { type: "string", description: "Aciklama" },
99
+ },
100
+ required: ["name", "schedule", "command"],
101
+ },
102
+ async execute(params) {
103
+ return await createCron(params);
104
+ },
105
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * delegate_task - Alt-agent'a gorev devret (v4.9.0)
3
+ *
4
+ * Hermes delegate_task'ina benzer. Bir alt-agent baslatip ozel bir gorev verir.
5
+ * Not: Bu basitlestirilmis versiyon - async sub-process olarak agent calistirir.
6
+ */
7
+
8
+ const { spawn } = require("child_process");
9
+ const path = require("path");
10
+ const os = require("os");
11
+
12
+ async function delegate({ task, agent = "general", timeoutMs = 60000 }) {
13
+ if (!task) return { success: false, error: "task gerekli" };
14
+
15
+ // Bu basitlestirilmis versiyon: kendi agent sistemi yerine
16
+ // natureco'nun REPL'ini sub-process olarak baslatip gorev verir
17
+ return new Promise((resolve) => {
18
+ const cliPath = path.resolve(__dirname, "..", "..", "bin", "natureco.js");
19
+ const args = ["ask", `"${task.replace(/"/g, '\\"')}"`];
20
+
21
+ let stdout = "";
22
+ let stderr = "";
23
+ const proc = spawn("node", [cliPath, ...args], {
24
+ timeout: timeoutMs,
25
+ env: { ...process.env },
26
+ });
27
+
28
+ proc.stdout.on("data", d => stdout += d.toString());
29
+ proc.stderr.on("data", d => stderr += d.toString());
30
+
31
+ proc.on("close", code => {
32
+ // natureco ask komutu genelde cevabi stdout'a yazar
33
+ const output = stdout.split("\n").filter(l => !l.startsWith("[") && l.trim()).join("\n").trim();
34
+ resolve({
35
+ success: code === 0,
36
+ task,
37
+ agent,
38
+ output: output || stderr,
39
+ exitCode: code,
40
+ });
41
+ });
42
+ proc.on("error", e => resolve({ success: false, error: e.message, task }));
43
+ });
44
+ }
45
+
46
+ module.exports = {
47
+ name: "delegate_task",
48
+ description: "Alt-agent'a ozel gorev devret. Sub-process olarak REPL calistirip gorev verir, cevabi dondurur.",
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {
52
+ task: { type: "string", description: "Alt-agent'in yapacagi gorev (ornek: 'README.md dosyasinin ilk 100 satirini ozetle')" },
53
+ agent: { type: "string", description: "Agent tipi (default: general)", enum: ["general", "explore", "review", "seo", "content", "security", "debugger", "translator"] },
54
+ timeoutMs: { type: "number", description: "Timeout ms (default 60000)" },
55
+ },
56
+ required: ["task"],
57
+ },
58
+ async execute(params) {
59
+ return await delegate(params);
60
+ },
61
+ };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * file_search - Glob pattern ile dosya arama (v4.9.0)
3
+ *
4
+ * Hermes dosya arama motoruna benzer.
5
+ */
6
+
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+ const os = require("os");
10
+
11
+ /**
12
+ * Recursive glob search
13
+ * @param pattern - Glob pattern (ornek: "glob" ile "glob" arasi)
14
+ * @param basePath - Aranacak dizin (default: cwd)
15
+ * @param maxResults - Max sonuc sayisi (default 100)
16
+ */
17
+ async function searchFiles({ pattern, basePath = null, maxResults = 100 }) {
18
+ if (!pattern) return { success: false, error: "pattern gerekli" };
19
+
20
+ const cwd = basePath || process.cwd();
21
+ const results = [];
22
+
23
+ // Basit glob regex donusumu (glob deseni)
24
+ const regexPattern = pattern
25
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
26
+ .replace(/\\\*\\\*/g, ".*")
27
+ .replace(/\\\*/g, "[^/]*")
28
+ .replace(/\\\?/g, "[^/]");
29
+
30
+ const regex = new RegExp("^" + regexPattern + "$");
31
+
32
+ function walk(dir, depth) {
33
+ if (depth > 8 || results.length >= maxResults) return;
34
+ let entries;
35
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
36
+ catch { return; }
37
+
38
+ for (const entry of entries) {
39
+ if (results.length >= maxResults) return;
40
+ if (entry.name.startsWith(".") && entry.name !== ".gitignore") continue;
41
+ if (entry.name === "node_modules") continue;
42
+
43
+ const fullPath = path.join(dir, entry.name);
44
+ const relative = path.relative(cwd, fullPath);
45
+
46
+ if (regex.test(relative) || regex.test(entry.name)) {
47
+ results.push({
48
+ path: fullPath,
49
+ relative,
50
+ name: entry.name,
51
+ type: entry.isDirectory() ? "directory" : "file",
52
+ size: entry.isFile() ? fs.statSync(fullPath).size : null,
53
+ });
54
+ }
55
+
56
+ if (entry.isDirectory()) {
57
+ walk(fullPath, depth + 1);
58
+ }
59
+ }
60
+ }
61
+
62
+ try {
63
+ walk(cwd, 0);
64
+ return { success: true, pattern, count: results.length, results };
65
+ } catch (e) {
66
+ return { success: false, error: e.message, pattern };
67
+ }
68
+ }
69
+
70
+ module.exports = {
71
+ name: "file_search",
72
+ description: "Glob pattern ile dosya/klasor arama. Hermes dosya arama motoru gibi. Default cwd arar.",
73
+ inputSchema: {
74
+ type: "object",
75
+ properties: {
76
+ pattern: { type: "string", description: "Glob pattern (ornek: glob)" },
77
+ basePath: { type: "string", description: "Aranacak dizin (default: cwd)" },
78
+ maxResults: { type: "number", description: "Max sonuc (default 100)" },
79
+ },
80
+ required: ["pattern"],
81
+ },
82
+ async execute(params) {
83
+ return await searchFiles(params);
84
+ },
85
+ };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * grep_search — İçerik araması (ripgrep tarzı) (v4.9.0)
3
+ *
4
+ * Hermes'ın search_files'ine benzer ama içerik arar.
5
+ * "TODO" kelimesini tüm src/ içinde ara gibi.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { spawn } = require('child_process');
11
+
12
+ /**
13
+ * Dosya içinde pattern ara
14
+ * @param pattern - Aranacak metin veya regex
15
+ * @param path - Aranacak dosya/klasör yolu
16
+ * @param caseSensitive - Case sensitive (default false)
17
+ * @param maxResults - Max sonuç (default 50)
18
+ */
19
+ async function grepSearch({ pattern, path: searchPath = null, caseSensitive = false, includePattern = null, maxResults = 50 }) {
20
+ if (!pattern) return { success: false, error: 'pattern gerekli' };
21
+
22
+ const cwd = searchPath || process.cwd();
23
+ // ripgrep varsa onu kullan, yoksa fallback grep
24
+ const useRipgrep = await checkCommand('rg');
25
+
26
+ if (useRipgrep) {
27
+ return await grepWithRipgrep(pattern, cwd, caseSensitive, includePattern, maxResults);
28
+ }
29
+ return await grepWithFallback(pattern, cwd, caseSensitive, maxResults);
30
+ }
31
+
32
+ async function checkCommand(cmd) {
33
+ return new Promise((resolve) => {
34
+ const proc = spawn('which', [cmd]);
35
+ proc.on('close', code => resolve(code === 0));
36
+ proc.on('error', () => resolve(false));
37
+ });
38
+ }
39
+
40
+ async function grepWithRipgrep(pattern, cwd, caseSensitive, includePattern, maxResults) {
41
+ return new Promise((resolve) => {
42
+ const args = [
43
+ '--json',
44
+ '--line-number',
45
+ caseSensitive ? '' : '--ignore-case',
46
+ includePattern ? `-g "${includePattern}"` : '',
47
+ '--no-heading',
48
+ ].filter(Boolean);
49
+ args.push(pattern);
50
+ args.push(cwd);
51
+
52
+ const proc = spawn('rg', args, { cwd });
53
+ let stdout = '';
54
+ let stderr = '';
55
+ proc.stdout.on('data', d => stdout += d.toString());
56
+ proc.stderr.on('data', d => stderr += d.toString());
57
+
58
+ proc.on('close', (code) => {
59
+ const results = [];
60
+ const lines = stdout.split('\n').filter(Boolean);
61
+ for (const line of lines) {
62
+ if (results.length >= maxResults) break;
63
+ try {
64
+ const obj = JSON.parse(line);
65
+ if (obj.type === 'match') {
66
+ results.push({
67
+ file: obj.data.path.text,
68
+ line: obj.data.line_number,
69
+ text: obj.data.lines.text.trim(),
70
+ });
71
+ }
72
+ } catch {}
73
+ }
74
+ resolve({ success: true, pattern, tool: 'ripgrep', count: results.length, results });
75
+ });
76
+ proc.on('error', (e) => resolve({ success: false, error: e.message }));
77
+ });
78
+ }
79
+
80
+ async function grepWithFallback(pattern, cwd, caseSensitive, maxResults) {
81
+ return new Promise((resolve) => {
82
+ const args = ['-r', '-n', caseSensitive ? '' : '-i'];
83
+ if (process.platform === 'darwin') args.push('-E');
84
+ else args.push('-E');
85
+ args.push(pattern);
86
+ args.push(cwd);
87
+
88
+ const proc = spawn('grep', args);
89
+ let stdout = '';
90
+ let stderr = '';
91
+ proc.stdout.on('data', d => stdout += d.toString());
92
+ proc.stderr.on('data', d => stderr += d.toString());
93
+
94
+ proc.on('close', (code) => {
95
+ const results = [];
96
+ const lines = stdout.split('\n').filter(Boolean);
97
+ for (const line of lines) {
98
+ if (results.length >= maxResults) break;
99
+ const match = line.match(/^(.+?):(\d+):(.*)$/);
100
+ if (match) {
101
+ results.push({ file: match[1], line: parseInt(match[2]), text: match[3] });
102
+ }
103
+ }
104
+ resolve({ success: true, pattern, tool: 'grep', count: results.length, results });
105
+ });
106
+ proc.on('error', (e) => resolve({ success: false, error: e.message }));
107
+ });
108
+ }
109
+
110
+ module.exports = {
111
+ name: 'grep_search',
112
+ description: 'Dosya içeriklerinde pattern ara (ripgrep veya grep). Örn: pattern="TODO", path="~/projects".',
113
+ inputSchema: {
114
+ type: 'object',
115
+ properties: {
116
+ pattern: { type: 'string', description: 'Aranacak metin veya regex' },
117
+ path: { type: 'string', description: 'Aranacak dosya/klasör yolu (default: cwd)' },
118
+ caseSensitive: { type: 'boolean', description: 'Büyük/küçük harf duyarlı (default: false)' },
119
+ includePattern: { type: 'string', description: 'Glob filter (örn: "*.js")' },
120
+ maxResults: { type: 'number', description: 'Max sonuç (default 50)' },
121
+ },
122
+ required: ['pattern'],
123
+ },
124
+ async execute(params) {
125
+ return await grepSearch(params);
126
+ },
127
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * http_request — HTTP GET/POST/PUT/DELETE isteği (v4.9.0)
3
+ *
4
+ * Hermes'ın http_request'ine benzer. API çağrıları için.
5
+ */
6
+
7
+ async function httpRequest({ url, method = 'GET', headers = {}, body = null, timeoutMs = 30000 }) {
8
+ if (!url) return { success: false, error: 'url gerekli' };
9
+
10
+ // fetch global olarak Node 18+'da var
11
+ if (typeof fetch !== 'function') {
12
+ return { success: false, error: 'fetch() Node 18+ gerektirir' };
13
+ }
14
+
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
17
+
18
+ try {
19
+ const options = {
20
+ method: method.toUpperCase(),
21
+ headers: { 'User-Agent': 'NatureCo-CLI/4.9.0', ...headers },
22
+ signal: controller.signal,
23
+ };
24
+ if (body && ['POST', 'PUT', 'PATCH'].includes(options.method)) {
25
+ if (typeof body === 'string') options.body = body;
26
+ else {
27
+ options.body = JSON.stringify(body);
28
+ if (!headers['Content-Type']) options.headers['Content-Type'] = 'application/json';
29
+ }
30
+ }
31
+
32
+ const response = await fetch(url, options);
33
+ clearTimeout(timeout);
34
+
35
+ const contentType = response.headers.get('content-type') || '';
36
+ let responseBody;
37
+ if (contentType.includes('application/json')) {
38
+ responseBody = await response.json();
39
+ } else {
40
+ responseBody = await response.text();
41
+ if (responseBody.length > 10000) {
42
+ responseBody = responseBody.slice(0, 10000) + '\n... (kesildi)';
43
+ }
44
+ }
45
+
46
+ return {
47
+ success: response.ok,
48
+ status: response.status,
49
+ statusText: response.statusText,
50
+ headers: Object.fromEntries(response.headers.entries()),
51
+ body: responseBody,
52
+ };
53
+ } catch (e) {
54
+ clearTimeout(timeout);
55
+ if (e.name === 'AbortError') return { success: false, error: `Timeout (${timeoutMs}ms)` };
56
+ return { success: false, error: e.message, url, method };
57
+ }
58
+ }
59
+
60
+ module.exports = {
61
+ name: 'http_request',
62
+ description: 'HTTP GET/POST/PUT/DELETE isteği. JSON veya text response döndürür. Örn: API\'lerden veri çekmek için.',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ url: { type: 'string', description: 'Hedef URL (https://api.example.com/data)' },
67
+ method: { type: 'string', description: 'HTTP metodu', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] },
68
+ headers: { type: 'object', description: 'HTTP header\'ları (Auth, Content-Type, vs.)' },
69
+ body: { type: 'string', description: 'Request body (JSON string veya object)' },
70
+ timeoutMs: { type: 'number', description: 'Timeout ms (default 30000)' },
71
+ },
72
+ required: ['url'],
73
+ },
74
+ async execute(params) {
75
+ return await httpRequest(params);
76
+ },
77
+ };