natureco-cli 5.9.6 → 5.10.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.9.6",
3
+ "version": "5.10.0",
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"
@@ -241,7 +241,7 @@ function extractFacts(messages, currentFacts) {
241
241
  return newFacts;
242
242
  }
243
243
 
244
- function apiRequest(providerUrl, providerApiKey, body, stream = false) {
244
+ function apiRequest(providerUrl, providerApiKey, body, stream = false, retries = 3) {
245
245
  return new Promise((resolve, reject) => {
246
246
  const isMM = isMiniMax(providerUrl);
247
247
  const endpoint = isMM
@@ -249,29 +249,38 @@ function apiRequest(providerUrl, providerApiKey, body, stream = false) {
249
249
  : isGemini(providerUrl)
250
250
  ? `${providerUrl.replace(/\/+$/, '')}/openai/chat/completions`
251
251
  : `${providerUrl.replace(/\/+$/, '')}/chat/completions`;
252
- const req = https.request(endpoint, {
253
- method: 'POST',
254
- headers: {
255
- 'Authorization': `Bearer ${providerApiKey}`,
256
- 'Content-Type': 'application/json',
257
- },
258
- timeout: 60000,
259
- }, (res) => {
260
- if (stream) { resolve(res); return; }
261
- let data = '';
262
- res.on('data', c => data += c);
263
- res.on('end', () => {
264
- if (res.statusCode >= 200 && res.statusCode < 300) {
265
- try { resolve(JSON.parse(data)); } catch (e) { reject(new Error('Parse hatası')); }
266
- } else {
267
- reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`));
268
- }
252
+ const doRequest = (attempt) => {
253
+ const req = https.request(endpoint, {
254
+ method: 'POST',
255
+ headers: {
256
+ 'Authorization': `Bearer ${providerApiKey}`,
257
+ 'Content-Type': 'application/json',
258
+ },
259
+ timeout: 60000,
260
+ }, (res) => {
261
+ if (stream) { resolve(res); return; }
262
+ let data = '';
263
+ res.on('data', c => data += c);
264
+ res.on('end', () => {
265
+ if (res.statusCode >= 200 && res.statusCode < 300) {
266
+ try { resolve(JSON.parse(data)); } catch (e) { reject(new Error('Parse hatası')); }
267
+ } else if (res.statusCode === 429 && attempt < retries) {
268
+ const delay = Math.pow(2, attempt) * 1000;
269
+ setTimeout(() => doRequest(attempt + 1), delay);
270
+ } else {
271
+ const msg = res.statusCode === 429
272
+ ? 'HTTP 429: API rate limit aşıldı. Lütfen bekleyin veya planınızı yükseltin.'
273
+ : `HTTP ${res.statusCode}: ${data.slice(0, 200)}`;
274
+ reject(new Error(msg));
275
+ }
276
+ });
269
277
  });
270
- });
271
- req.on('error', reject);
272
- req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
273
- req.write(JSON.stringify(body));
274
- req.end();
278
+ req.on('error', reject);
279
+ req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
280
+ req.write(JSON.stringify(body));
281
+ req.end();
282
+ };
283
+ doRequest(0);
275
284
  });
276
285
  }
277
286
 
@@ -527,10 +536,21 @@ async function processToolCalls(toolCalls, onToolCall) {
527
536
  }
528
537
 
529
538
  // Notify UI done + build messages
539
+ // v5.9.7: Skip internal meta-tools (_loop_warning, _no_progress) from tool messages
540
+ // Inject loop warning as user message instead (Gemini requires real tool names)
541
+ // Gemini also requires 'name' field in tool response messages
530
542
  const messages = [];
531
543
  for (const { name, id, result } of results) {
532
544
  if (onToolCall) onToolCall({ name, args: null, status: 'done', result });
533
545
 
546
+ if (name === '_loop_warning' || name === '_no_progress') {
547
+ if (name === '_loop_warning') {
548
+ const warnContent = typeof result?.result === 'string' ? result.result : '';
549
+ if (warnContent) messages.push({ role: 'user', content: '[System: ' + warnContent + ']' });
550
+ }
551
+ continue;
552
+ }
553
+
534
554
  let content;
535
555
  if (result.error) {
536
556
  content = JSON.stringify({ error: result.error });
@@ -544,7 +564,7 @@ async function processToolCalls(toolCalls, onToolCall) {
544
564
  }
545
565
  }
546
566
 
547
- messages.push({ role: 'tool', tool_call_id: id, content });
567
+ messages.push({ role: 'tool', tool_call_id: id, name, content });
548
568
  }
549
569
 
550
570
  return messages;
@@ -0,0 +1,55 @@
1
+ const APPROVAL_QUEUE = [];
2
+ const PENDING_APPROVALS = new Map();
3
+
4
+ async function approval(params) {
5
+ const { action, operationId, response } = params;
6
+
7
+ if (action === 'request') {
8
+ const { operation, description, details } = params;
9
+ if (!operation || !description) return { success: false, error: 'operation ve description gerekli' };
10
+ const id = operationId || `approval_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
11
+ const entry = { id, operation, description, details, status: 'pending', createdAt: new Date().toISOString() };
12
+ PENDING_APPROVALS.set(id, entry);
13
+ APPROVAL_QUEUE.push(entry);
14
+ return { success: true, approvalId: id, message: 'Onay bekliyor: ' + description, status: 'pending' };
15
+ }
16
+
17
+ if (action === 'respond') {
18
+ if (!operationId) return { success: false, error: 'operationId gerekli' };
19
+ const entry = PENDING_APPROVALS.get(operationId);
20
+ if (!entry) return { success: false, error: 'Onay bulunamadi: ' + operationId };
21
+ entry.status = response === 'approve' ? 'approved' : 'rejected';
22
+ entry.respondedAt = new Date().toISOString();
23
+ return { success: true, approvalId: operationId, status: entry.status, message: 'Islem ' + entry.status };
24
+ }
25
+
26
+ if (action === 'list') {
27
+ return { success: true, queue: APPROVAL_QUEUE.filter(e => e.status === 'pending') };
28
+ }
29
+
30
+ if (action === 'status') {
31
+ if (!operationId) return { success: false, error: 'operationId gerekli' };
32
+ const entry = PENDING_APPROVALS.get(operationId);
33
+ return { success: true, entry: entry || null };
34
+ }
35
+
36
+ return { success: false, error: 'Gecersiz action: ' + action };
37
+ }
38
+
39
+ module.exports = {
40
+ name: 'approval',
41
+ description: 'Guvenlik onay kuyrugu: tehlikeli islemler icin onay iste/yanitla/listele.',
42
+ inputSchema: {
43
+ type: 'object',
44
+ properties: {
45
+ action: { type: 'string', description: 'request, respond, list, status', enum: ['request', 'respond', 'list', 'status'] },
46
+ operation: { type: 'string', description: '(action=request) Islem adi' },
47
+ description: { type: 'string', description: '(action=request) Aciklama' },
48
+ details: { type: 'string', description: '(action=request) Detayli aciklama' },
49
+ operationId: { type: 'string', description: '(action=respond/status) Onay ID' },
50
+ response: { type: 'string', description: '(action=respond) approve veya reject', enum: ['approve', 'reject'] },
51
+ },
52
+ required: ['action'],
53
+ },
54
+ async execute(params) { return await approval(params); },
55
+ };
@@ -0,0 +1,83 @@
1
+ const { spawn } = require('child_process');
2
+ const path = require('path');
3
+
4
+ const ASYNC_TASKS = new Map();
5
+
6
+ async function asyncDelegation(params) {
7
+ const { action, taskId, prompt, toolset, model } = params;
8
+
9
+ if (action === 'start') {
10
+ if (!prompt) return { success: false, error: 'prompt gerekli' };
11
+ const id = taskId || `async_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
12
+
13
+ const naturecoBin = process.argv[1] || 'natureco';
14
+ const args = ['ask', prompt];
15
+ if (model) args.push('--model', model);
16
+ if (toolset) args.push('--toolset', toolset);
17
+
18
+ const child = spawn(process.execPath, [naturecoBin, ...args], {
19
+ stdio: ['ignore', 'pipe', 'pipe'],
20
+ detached: true,
21
+ env: { ...process.env, NATURECO_ASYNC: '1' },
22
+ });
23
+
24
+ let output = '';
25
+ child.stdout.on('data', d => output += d);
26
+ child.stderr.on('data', d => output += d);
27
+
28
+ const task = { id, prompt, model, toolset, status: 'running', startedAt: new Date().toISOString(), child, output: '' };
29
+ ASYNC_TASKS.set(id, task);
30
+
31
+ child.on('close', (code) => {
32
+ task.status = code === 0 ? 'completed' : 'failed';
33
+ task.exitCode = code;
34
+ task.output = output.slice(0, 5000);
35
+ task.completedAt = new Date().toISOString();
36
+ });
37
+
38
+ child.unref();
39
+
40
+ return { success: true, taskId: id, status: 'running', message: 'Async gorev baslatildi: ' + id };
41
+ }
42
+
43
+ if (action === 'status') {
44
+ if (!taskId) {
45
+ const tasks = [];
46
+ for (const [id, t] of ASYNC_TASKS) {
47
+ tasks.push({ id, status: t.status, prompt: t.prompt?.slice(0, 100), startedAt: t.startedAt, completedAt: t.completedAt });
48
+ }
49
+ return { success: true, tasks };
50
+ }
51
+ const task = ASYNC_TASKS.get(taskId);
52
+ if (!task) return { success: false, error: 'Gorev bulunamadi: ' + taskId };
53
+ return { success: true, taskId, status: task.status, output: task.output, startedAt: task.startedAt, completedAt: task.completedAt, exitCode: task.exitCode };
54
+ }
55
+
56
+ if (action === 'cancel') {
57
+ if (!taskId) return { success: false, error: 'taskId gerekli' };
58
+ const task = ASYNC_TASKS.get(taskId);
59
+ if (!task) return { success: false, error: 'Gorev bulunamadi' };
60
+ try { task.child.kill(); } catch {}
61
+ task.status = 'cancelled';
62
+ return { success: true, taskId, status: 'cancelled' };
63
+ }
64
+
65
+ return { success: false, error: 'Gecersiz action: ' + action + ' (start, status, cancel)' };
66
+ }
67
+
68
+ module.exports = {
69
+ name: 'async_delegation',
70
+ description: 'Arkaplanda async gorev calistirma: start/status/cancel. Uzun suren islemler icin.',
71
+ inputSchema: {
72
+ type: 'object',
73
+ properties: {
74
+ action: { type: 'string', description: 'start, status, cancel', enum: ['start', 'status', 'cancel'] },
75
+ taskId: { type: 'string', description: 'Gorev ID (status/cancel icin gerekli)' },
76
+ prompt: { type: 'string', description: '(start) Gorev promptu' },
77
+ toolset: { type: 'string', description: '(start) Kullanilacak toolset' },
78
+ model: { type: 'string', description: '(start) Model adi' },
79
+ },
80
+ required: ['action'],
81
+ },
82
+ async execute(params) { return await asyncDelegation(params); },
83
+ };
@@ -0,0 +1,88 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const BLUEPRINTS_DIR = path.join(process.env.HOME || process.env.USERPROFILE || __dirname, '.natureco', 'blueprints');
5
+
6
+ function ensureDir(dir) { try { fs.mkdirSync(dir, { recursive: true }); } catch {} }
7
+
8
+ async function blueprint(params) {
9
+ const { action, name, description, steps, variables, data } = params;
10
+ ensureDir(BLUEPRINTS_DIR);
11
+
12
+ if (action === 'create') {
13
+ if (!name || !steps) return { success: false, error: 'name ve steps gerekli' };
14
+ const bp = {
15
+ name, description: description || '',
16
+ variables: variables || [],
17
+ steps: Array.isArray(steps) ? steps : [steps],
18
+ createdAt: new Date().toISOString(),
19
+ updatedAt: new Date().toISOString(),
20
+ };
21
+ const filePath = path.join(BLUEPRINTS_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
22
+ fs.writeFileSync(filePath, JSON.stringify(bp, null, 2));
23
+ return { success: true, name, stepCount: bp.steps.length, file: filePath, message: 'Blueprint olusturuldu: ' + name };
24
+ }
25
+
26
+ if (action === 'load') {
27
+ if (!name) return { success: false, error: 'name gerekli' };
28
+ const filePath = path.join(BLUEPRINTS_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
29
+ if (!fs.existsSync(filePath)) return { success: false, error: 'Blueprint bulunamadi: ' + name };
30
+ const bp = JSON.parse(fs.readFileSync(filePath, 'utf8'));
31
+ return { success: true, ...bp };
32
+ }
33
+
34
+ if (action === 'list') {
35
+ const files = fs.readdirSync(BLUEPRINTS_DIR).filter(f => f.endsWith('.json'));
36
+ const list = files.map(f => {
37
+ try {
38
+ const bp = JSON.parse(fs.readFileSync(path.join(BLUEPRINTS_DIR, f), 'utf8'));
39
+ return { name: bp.name, description: bp.description, stepCount: bp.steps?.length || 0, createdAt: bp.createdAt };
40
+ } catch { return null; }
41
+ }).filter(Boolean);
42
+ return { success: true, blueprints: list };
43
+ }
44
+
45
+ if (action === 'execute') {
46
+ if (!name) return { success: false, error: 'name gerekli' };
47
+ const filePath = path.join(BLUEPRINTS_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
48
+ if (!fs.existsSync(filePath)) return { success: false, error: 'Blueprint bulunamadi: ' + name };
49
+ const bp = JSON.parse(fs.readFileSync(filePath, 'utf8'));
50
+ const variables = data || {};
51
+ const renderedSteps = bp.steps.map(step => {
52
+ let content = typeof step === 'string' ? step : (step.content || step.prompt || '');
53
+ for (const [k, v] of Object.entries(variables)) {
54
+ content = content.replace(new RegExp(`\\{\\{\\s*${k}\\s*\\}\\}`, 'g'), v);
55
+ }
56
+ return content;
57
+ });
58
+ return { success: true, name, executed: true, steps: renderedSteps, stepCount: renderedSteps.length };
59
+ }
60
+
61
+ if (action === 'delete') {
62
+ if (!name) return { success: false, error: 'name gerekli' };
63
+ const filePath = path.join(BLUEPRINTS_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
64
+ if (!fs.existsSync(filePath)) return { success: false, error: 'Blueprint bulunamadi' };
65
+ fs.unlinkSync(filePath);
66
+ return { success: true, message: name + ' silindi' };
67
+ }
68
+
69
+ return { success: false, error: 'Gecersiz action: ' + action + ' (create, load, list, execute, delete)' };
70
+ }
71
+
72
+ module.exports = {
73
+ name: 'blueprint',
74
+ description: 'Tekrar kullanilabilir workflow blueprint: create/load/list/execute/delete. Adimli is akisi ve degisken ikamesi.',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ action: { type: 'string', description: 'create, load, list, execute, delete', enum: ['create', 'load', 'list', 'execute', 'delete'] },
79
+ name: { type: 'string', description: 'Blueprint adi' },
80
+ description: { type: 'string', description: '(create) Aciklama' },
81
+ steps: { type: 'array', description: '(create) Adimlar (string veya obje dizisi)', items: { type: 'string' } },
82
+ variables: { type: 'array', description: '(create) Degisken tanimlari', items: { type: 'string' } },
83
+ data: { type: 'object', description: '(execute) Degisken degerleri' },
84
+ },
85
+ required: ['action'],
86
+ },
87
+ async execute(params) { return await blueprint(params); },
88
+ };
@@ -0,0 +1,66 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const CHECKPOINT_DIR = path.join(process.env.HOME || process.env.USERPROFILE || __dirname, '.natureco', 'checkpoints');
5
+
6
+ function ensureDir(dir) { try { fs.mkdirSync(dir, { recursive: true }); } catch {} }
7
+
8
+ async function checkpoint(params) {
9
+ const { action, name, data } = params;
10
+ ensureDir(CHECKPOINT_DIR);
11
+
12
+ if (action === 'save') {
13
+ if (!name) return { success: false, error: 'checkpoint name gerekli' };
14
+ const filePath = path.join(CHECKPOINT_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
15
+ const entry = {
16
+ name, savedAt: new Date().toISOString(),
17
+ data: data || {},
18
+ };
19
+ fs.writeFileSync(filePath, JSON.stringify(entry, null, 2));
20
+ return { success: true, name, file: filePath, savedAt: entry.savedAt };
21
+ }
22
+
23
+ if (action === 'load') {
24
+ if (!name) return { success: false, error: 'checkpoint name gerekli' };
25
+ const filePath = path.join(CHECKPOINT_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
26
+ if (!fs.existsSync(filePath)) return { success: false, error: 'Checkpoint bulunamadi: ' + name };
27
+ const entry = JSON.parse(fs.readFileSync(filePath, 'utf8'));
28
+ return { success: true, name: entry.name, savedAt: entry.savedAt, data: entry.data };
29
+ }
30
+
31
+ if (action === 'list') {
32
+ const files = fs.readdirSync(CHECKPOINT_DIR).filter(f => f.endsWith('.json'));
33
+ const list = files.map(f => {
34
+ try {
35
+ const entry = JSON.parse(fs.readFileSync(path.join(CHECKPOINT_DIR, f), 'utf8'));
36
+ return { name: entry.name, savedAt: entry.savedAt, size: Object.keys(entry.data || {}).length };
37
+ } catch { return null; }
38
+ }).filter(Boolean);
39
+ return { success: true, checkpoints: list };
40
+ }
41
+
42
+ if (action === 'delete') {
43
+ if (!name) return { success: false, error: 'checkpoint name gerekli' };
44
+ const filePath = path.join(CHECKPOINT_DIR, name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.json');
45
+ if (!fs.existsSync(filePath)) return { success: false, error: 'Checkpoint bulunamadi' };
46
+ fs.unlinkSync(filePath);
47
+ return { success: true, message: name + ' silindi' };
48
+ }
49
+
50
+ return { success: false, error: 'Gecersiz action: ' + action };
51
+ }
52
+
53
+ module.exports = {
54
+ name: 'checkpoint',
55
+ description: 'Oturum/fikir checkpoinit: save/load/list/delete. Islem durumunu kaydet ve geri yukle.',
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {
59
+ action: { type: 'string', description: 'save, load, list, delete', enum: ['save', 'load', 'list', 'delete'] },
60
+ name: { type: 'string', description: 'Checkpoint adi' },
61
+ data: { type: 'object', description: '(save) Kaydedilecek veri' },
62
+ },
63
+ required: ['action'],
64
+ },
65
+ async execute(params) { return await checkpoint(params); },
66
+ };
@@ -0,0 +1,31 @@
1
+ async function clarify(params) {
2
+ const { question, type, options, context } = params;
3
+ if (!question) return { success: false, error: 'question gerekli' };
4
+
5
+ const result = {
6
+ clarification: true,
7
+ question,
8
+ type: type || 'text',
9
+ context: context || undefined,
10
+ options: Array.isArray(options) && options.length > 0 ? options : undefined,
11
+ instruction: 'Lutfen bu soruyu yanitlayin. Cevabiniz sonraki islemlerde kullanilacak.',
12
+ };
13
+
14
+ return { success: true, ...result };
15
+ }
16
+
17
+ module.exports = {
18
+ name: 'clarify',
19
+ description: 'Kullanicidan netlestirme sorusu sorar: eksik bilgi, secim, onay veya aciklama istemek icin.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ question: { type: 'string', description: 'Kullaniciya sorulacak soru' },
24
+ type: { type: 'string', description: 'Cevap tipi: text, choice, confirm, explanation', enum: ['text', 'choice', 'confirm', 'explanation'] },
25
+ options: { type: 'array', description: '(type=choice icin) Secenekler', items: { type: 'string' } },
26
+ context: { type: 'string', description: 'Sorunun baglam aciklamasi' },
27
+ },
28
+ required: ['question'],
29
+ },
30
+ async execute(params) { return await clarify(params); },
31
+ };
@@ -0,0 +1,141 @@
1
+ const { spawn, execSync } = require('child_process');
2
+ const os = require('os');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const PLATFORM = os.platform();
7
+
8
+ async function computerUse(params) {
9
+ const { action, x, y, key, text, button, clicks, query, file } = params;
10
+
11
+ if (action === 'screenshot') {
12
+ const outputFile = file || path.join(os.tmpdir(), 'natureco_screen_' + Date.now() + '.png');
13
+ if (PLATFORM === 'darwin') {
14
+ execSync('screencapture -x "' + outputFile + '"', { timeout: 5000 });
15
+ } else if (PLATFORM === 'win32') {
16
+ execSync('powershell -Command "Add-Type -AssemblyName System.Windows.Forms; $bmp = [System.Drawing.Bitmap]::new([System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width, [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height); $g = [System.Drawing.Graphics]::FromImage($bmp); $g.CopyFromScreen(0, 0, 0, 0, $bmp.Size); $bmp.Save(\\"' + outputFile + '\\", [System.Drawing.Imaging.ImageFormat]::Png)"', { timeout: 10000 });
17
+ } else {
18
+ execSync('import -window root "' + outputFile + '"', { timeout: 5000 });
19
+ }
20
+ return { success: true, file: outputFile, platform: PLATFORM, note: 'Ekran goruntusu alindi: ' + outputFile };
21
+ }
22
+
23
+ if (action === 'click') {
24
+ if (typeof x !== 'number' || typeof y !== 'number') return { success: false, error: 'x ve y gerekli' };
25
+ const btn = button || 'left';
26
+ if (PLATFORM === 'darwin') {
27
+ execSync('osascript -e \'tell application "System Events" to click at {' + x + ', ' + y + '}\'', { timeout: 5000 });
28
+ } else if (PLATFORM === 'win32') {
29
+ const c = clicks || 1;
30
+ execSync('powershell -Command "[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(' + x + ', ' + y + '); [System.Windows.Forms.SendKeys]::SendWait(\\"' + (c > 1 ? '{DOUBLECLICK}' : '{CLICK}') + '\\")"', { timeout: 5000 });
31
+ } else {
32
+ execSync('xdotool mousemove ' + x + ' ' + y + ' click 1', { timeout: 5000 });
33
+ }
34
+ return { success: true, action: 'click', x, y, button };
35
+ }
36
+
37
+ if (action === 'type') {
38
+ if (!text) return { success: false, error: 'text gerekli' };
39
+ if (PLATFORM === 'darwin') {
40
+ const safeText = text.replace(/"/g, '\\"');
41
+ execSync('osascript -e \'tell application "System Events" to keystroke "' + safeText + '"\'', { timeout: 5000 });
42
+ } else if (PLATFORM === 'win32') {
43
+ execSync('powershell -Command "[System.Windows.Forms.SendKeys]::SendWait(\\"' + text.replace(/[{}()^+% ~]/g, '{$&}') + '\\")"', { timeout: 5000 });
44
+ } else {
45
+ execSync('xdotool type "' + text.replace(/"/g, '\\"') + '"', { timeout: 5000 });
46
+ }
47
+ return { success: true, action: 'type', text };
48
+ }
49
+
50
+ if (action === 'keypress') {
51
+ if (!key) return { success: false, error: 'key gerekli' };
52
+ if (PLATFORM === 'darwin') {
53
+ const keyMap = { enter: 'return', tab: 'tab', escape: 'escape', up: 'up', down: 'down', left: 'left', right: 'right', backspace: 'delete', delete: 'forwarddelete' };
54
+ const k = keyMap[key.toLowerCase()] || key;
55
+ execSync('osascript -e \'tell application "System Events" to key code ' + (isNaN(k) ? '"' + k + '"' : k) + '\'', { timeout: 5000 });
56
+ } else if (PLATFORM === 'win32') {
57
+ const keyMap = { enter: '{ENTER}', tab: '{TAB}', escape: '{ESC}', up: '{UP}', down: '{DOWN}', left: '{LEFT}', right: '{RIGHT}', backspace: '{BACKSPACE}', delete: '{DELETE}' };
58
+ execSync('powershell -Command "[System.Windows.Forms.SendKeys]::SendWait(\\"' + (keyMap[key.toLowerCase()] || key) + '\\")"', { timeout: 5000 });
59
+ } else {
60
+ execSync('xdotool key ' + key, { timeout: 5000 });
61
+ }
62
+ return { success: true, action: 'keypress', key };
63
+ }
64
+
65
+ if (action === 'mouse_move') {
66
+ if (typeof x !== 'number' || typeof y !== 'number') return { success: false, error: 'x ve y gerekli' };
67
+ if (PLATFORM === 'darwin') {
68
+ execSync('osascript -e \'tell application "System Events" to set position of mouse to {' + x + ', ' + y + '}\'', { timeout: 5000 });
69
+ } else if (PLATFORM === 'win32') {
70
+ execSync('powershell -Command "[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(' + x + ', ' + y + ')"', { timeout: 5000 });
71
+ } else {
72
+ execSync('xdotool mousemove ' + x + ' ' + y, { timeout: 5000 });
73
+ }
74
+ return { success: true, action: 'mouse_move', x, y };
75
+ }
76
+
77
+ if (action === 'mouse_position') {
78
+ if (PLATFORM === 'darwin') {
79
+ const out = execSync('osascript -e \'tell application "System Events" to return position of mouse\'', { timeout: 5000 }).toString().trim();
80
+ const [mx, my] = out.split(', ').map(Number);
81
+ return { success: true, x: mx, y: my };
82
+ } else if (PLATFORM === 'win32') {
83
+ const out = execSync('powershell -Command "[System.Windows.Forms.Cursor]::Position.X.ToString() + \\", \\" + [System.Windows.Forms.Cursor]::Position.Y.ToString()"', { timeout: 5000 }).toString().trim();
84
+ const [mx, my] = out.split(', ').map(Number);
85
+ return { success: true, x: mx, y: my };
86
+ } else {
87
+ const out = execSync('xdotool getmouselocation', { timeout: 5000 }).toString().trim();
88
+ const mx = parseInt(out.match(/x:(\d+)/)?.[1] || '0');
89
+ const my = parseInt(out.match(/y:(\d+)/)?.[1] || '0');
90
+ return { success: true, x: mx, y: my };
91
+ }
92
+ }
93
+
94
+ if (action === 'scroll') {
95
+ if (typeof y !== 'number') return { success: false, error: 'y (pixels) gerekli' };
96
+ if (PLATFORM === 'darwin') {
97
+ execSync('osascript -e \'tell application "System Events" to scroll (current application)\' &', { timeout: 5000 }); // simplified
98
+ return { success: true, action: 'scroll', y, note: 'macOS scroll icin Accessibility izni gerekli' };
99
+ } else {
100
+ execSync('xdotool click ' + (y < 0 ? '4' : '5') + ' --repeat ' + Math.abs(Math.ceil(y / 50)), { timeout: 5000 });
101
+ return { success: true, action: 'scroll', y };
102
+ }
103
+ }
104
+
105
+ if (action === 'info') {
106
+ const displays = [];
107
+ try {
108
+ if (PLATFORM === 'darwin') {
109
+ const out = execSync('system_profiler SPDisplaysDataType 2>/dev/null | grep Resolution', { timeout: 5000 }).toString();
110
+ for (const line of out.trim().split('\n')) {
111
+ const m = line.match(/(\d+) x (\d+)/);
112
+ if (m) displays.push({ width: parseInt(m[1]), height: parseInt(m[2]) });
113
+ }
114
+ }
115
+ } catch {}
116
+ return { success: true, platform: PLATFORM, displays: displays.length > 0 ? displays : undefined, note: 'Ekran bilgisi' };
117
+ }
118
+
119
+ return { success: false, error: 'Gecersiz action: ' + action + ' (screenshot, click, type, keypress, mouse_move, mouse_position, scroll, info)' };
120
+ }
121
+
122
+ module.exports = {
123
+ name: 'computer_use',
124
+ description: 'GUI otomasyonu: screenshot, click, type, keypress, mouse_move, mouse_position, scroll, info. macOS/Windows/Linux.',
125
+ inputSchema: {
126
+ type: 'object',
127
+ properties: {
128
+ action: { type: 'string', description: 'screenshot, click, type, keypress, mouse_move, mouse_position, scroll, info', enum: ['screenshot', 'click', 'type', 'keypress', 'mouse_move', 'mouse_position', 'scroll', 'info'] },
129
+ x: { type: 'number', description: 'X koordinati' },
130
+ y: { type: 'number', description: 'Y koordinati' },
131
+ button: { type: 'string', description: 'Fare tusu: left, right, middle (default: left)' },
132
+ clicks: { type: 'number', description: 'Tiklama sayisi (default: 1)' },
133
+ key: { type: 'string', description: '(keypress) Tus adi: enter, tab, escape, up, down, left, right, backspace, delete' },
134
+ text: { type: 'string', description: '(type) Yazilacak metin' },
135
+ query: { type: 'string', description: 'Arama sorgusu' },
136
+ file: { type: 'string', description: '(screenshot) Kayit dosyasi' },
137
+ },
138
+ required: ['action'],
139
+ },
140
+ async execute(params) { return await computerUse(params); },
141
+ };
@@ -0,0 +1,65 @@
1
+ const https = require('https');
2
+
3
+ async function discord(params) {
4
+ const { action, webhookUrl, channel, message, username, avatarUrl, embeds } = params;
5
+
6
+ if (action === 'send_webhook') {
7
+ if (!webhookUrl || !message) return { success: false, error: 'webhookUrl ve message gerekli' };
8
+ const payload = { content: message };
9
+ if (username) payload.username = username;
10
+ if (avatarUrl) payload.avatar_url = avatarUrl;
11
+ if (embeds) payload.embeds = embeds;
12
+
13
+ return new Promise((resolve) => {
14
+ const urlObj = new URL(webhookUrl);
15
+ const data = JSON.stringify(payload);
16
+ const req = https.request(webhookUrl, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
19
+ timeout: 10000,
20
+ }, (res) => {
21
+ let body = '';
22
+ res.on('data', c => body += c);
23
+ res.on('end', () => {
24
+ if (res.statusCode === 204 || res.statusCode === 200) {
25
+ resolve({ success: true, message: 'Mesaj gonderildi' });
26
+ } else {
27
+ resolve({ success: false, error: `HTTP ${res.statusCode}: ${body.slice(0, 200)}` });
28
+ }
29
+ });
30
+ });
31
+ req.on('error', (e) => resolve({ success: false, error: e.message }));
32
+ req.on('timeout', () => { req.destroy(); resolve({ success: false, error: 'Timeout' }); });
33
+ req.write(data);
34
+ req.end();
35
+ });
36
+ }
37
+
38
+ if (action === 'format_message') {
39
+ const parts = [];
40
+ if (channel) parts.push(`**#${channel}**`);
41
+ if (message) parts.push(message);
42
+ return { success: true, formatted: parts.join('\n') };
43
+ }
44
+
45
+ return { success: false, error: 'Gecersiz action: ' + action + ' (desteklenen: send_webhook, format_message)' };
46
+ }
47
+
48
+ module.exports = {
49
+ name: 'discord',
50
+ description: 'Discord mesaji gonderme: webhook ile. Ayrica mesaj formatlama yardimcisi.',
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {
54
+ action: { type: 'string', description: 'send_webhook, format_message', enum: ['send_webhook', 'format_message'] },
55
+ webhookUrl: { type: 'string', description: '(send_webhook) Discord webhook URL' },
56
+ message: { type: 'string', description: 'Mesaj icerigi' },
57
+ channel: { type: 'string', description: 'Opsiyonel: kanal adi (formatlama icin)' },
58
+ username: { type: 'string', description: 'Opsiyonel: webhook goruntulenen ad' },
59
+ avatarUrl: { type: 'string', description: 'Opsiyonel: webhook avatar URL' },
60
+ embeds: { type: 'array', description: 'Opsiyonel: Discord embed nesneleri', items: { type: 'object' } },
61
+ },
62
+ required: ['action'],
63
+ },
64
+ async execute(params) { return await discord(params); },
65
+ };