natureco-cli 2.23.29 → 2.23.31

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.
Files changed (111) hide show
  1. package/README.md +94 -11
  2. package/bin/natureco.js +495 -94
  3. package/package.json +1 -1
  4. package/src/commands/acp.js +39 -0
  5. package/src/commands/admin-rpc.js +302 -0
  6. package/src/commands/agent.js +280 -0
  7. package/src/commands/agents.js +114 -30
  8. package/src/commands/approvals.js +214 -0
  9. package/src/commands/backup.js +124 -0
  10. package/src/commands/bonjour.js +167 -0
  11. package/src/commands/browser.js +815 -0
  12. package/src/commands/capability.js +237 -0
  13. package/src/commands/channels.js +422 -267
  14. package/src/commands/chat.js +5 -8
  15. package/src/commands/clawbot.js +19 -0
  16. package/src/commands/clickclack.js +130 -0
  17. package/src/commands/code.js +3 -2
  18. package/src/commands/commitments.js +148 -0
  19. package/src/commands/completion.js +84 -0
  20. package/src/commands/config.js +219 -30
  21. package/src/commands/configure.js +110 -0
  22. package/src/commands/crestodian.js +92 -0
  23. package/src/commands/cron.js +239 -19
  24. package/src/commands/daemon.js +90 -0
  25. package/src/commands/dashboard.js +47 -374
  26. package/src/commands/device-pair.js +248 -0
  27. package/src/commands/devices.js +137 -0
  28. package/src/commands/directory.js +179 -0
  29. package/src/commands/dns.js +196 -0
  30. package/src/commands/docs.js +136 -0
  31. package/src/commands/doctor.js +143 -492
  32. package/src/commands/exec-policy.js +80 -0
  33. package/src/commands/gateway-server.js +1155 -24
  34. package/src/commands/gateway.js +492 -249
  35. package/src/commands/health.js +148 -0
  36. package/src/commands/help.js +24 -25
  37. package/src/commands/hooks.js +141 -87
  38. package/src/commands/imessage.js +128 -14
  39. package/src/commands/infer.js +1474 -0
  40. package/src/commands/irc.js +64 -15
  41. package/src/commands/logs.js +122 -99
  42. package/src/commands/mattermost.js +114 -12
  43. package/src/commands/mcp.js +121 -309
  44. package/src/commands/memory-cmd.js +134 -1
  45. package/src/commands/memory.js +128 -0
  46. package/src/commands/message.js +720 -134
  47. package/src/commands/migrate.js +213 -2
  48. package/src/commands/models.js +39 -1
  49. package/src/commands/node.js +98 -0
  50. package/src/commands/nodes.js +362 -0
  51. package/src/commands/oc-path.js +200 -0
  52. package/src/commands/onboard.js +129 -0
  53. package/src/commands/open-prose.js +67 -0
  54. package/src/commands/pairing.js +108 -107
  55. package/src/commands/path.js +206 -0
  56. package/src/commands/plugins.js +35 -1
  57. package/src/commands/policy.js +176 -0
  58. package/src/commands/proxy.js +306 -0
  59. package/src/commands/qr.js +70 -0
  60. package/src/commands/reset.js +101 -94
  61. package/src/commands/sandbox.js +125 -0
  62. package/src/commands/secrets.js +201 -0
  63. package/src/commands/sessions.js +110 -51
  64. package/src/commands/setup.js +102 -543
  65. package/src/commands/signal.js +447 -18
  66. package/src/commands/skills.js +67 -1
  67. package/src/commands/sms.js +123 -19
  68. package/src/commands/status.js +101 -127
  69. package/src/commands/system.js +53 -0
  70. package/src/commands/tasks.js +208 -100
  71. package/src/commands/terminal.js +139 -0
  72. package/src/commands/thread-ownership.js +157 -0
  73. package/src/commands/transcripts.js +95 -0
  74. package/src/commands/tui.js +41 -0
  75. package/src/commands/uninstall.js +73 -92
  76. package/src/commands/update.js +146 -91
  77. package/src/commands/voice.js +82 -0
  78. package/src/commands/vydra.js +98 -0
  79. package/src/commands/webhooks.js +58 -66
  80. package/src/commands/wiki.js +783 -0
  81. package/src/commands/workboard.js +207 -0
  82. package/src/tools/audio_understanding.js +154 -0
  83. package/src/tools/browser.js +112 -0
  84. package/src/tools/canvas.js +104 -0
  85. package/src/tools/document_extract.js +84 -0
  86. package/src/tools/duckduckgo.js +54 -0
  87. package/src/tools/exa_search.js +66 -0
  88. package/src/tools/firecrawl.js +104 -0
  89. package/src/tools/image_generation.js +99 -0
  90. package/src/tools/llm_task.js +118 -0
  91. package/src/tools/media_understanding.js +128 -0
  92. package/src/tools/music_generation.js +113 -0
  93. package/src/tools/parallel_search.js +77 -0
  94. package/src/tools/phone_control.js +80 -0
  95. package/src/tools/phone_control_enhanced.js +184 -0
  96. package/src/tools/searxng.js +61 -0
  97. package/src/tools/speech_to_text.js +135 -0
  98. package/src/tools/text_to_speech.js +105 -0
  99. package/src/tools/thread_ownership.js +88 -0
  100. package/src/tools/video_generation.js +72 -0
  101. package/src/tools/web_readability.js +104 -0
  102. package/src/utils/agents-md.js +85 -0
  103. package/src/utils/api.js +39 -40
  104. package/src/utils/format.js +144 -0
  105. package/src/utils/headless.js +2 -1
  106. package/src/utils/memory.js +200 -0
  107. package/src/utils/parallel-tools.js +106 -0
  108. package/src/utils/sub-agent.js +148 -0
  109. package/src/utils/token-budget.js +304 -0
  110. package/src/utils/tool-runner.js +7 -5
  111. package/src/utils/web-fetch.js +107 -0
@@ -0,0 +1,207 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { getConfig, saveConfig } = require('../utils/config');
6
+
7
+ const BOARD_FILE = path.join(os.homedir(), '.natureco', 'data', 'workboard.json');
8
+
9
+ function loadBoard() {
10
+ try {
11
+ if (fs.existsSync(BOARD_FILE)) {
12
+ return JSON.parse(fs.readFileSync(BOARD_FILE, 'utf8'));
13
+ }
14
+ } catch {}
15
+ return { columns: ['todo', 'in-progress', 'done'], items: [] };
16
+ }
17
+
18
+ function saveBoard(board) {
19
+ const dir = path.dirname(BOARD_FILE);
20
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
21
+ fs.writeFileSync(BOARD_FILE, JSON.stringify(board, null, 2));
22
+ }
23
+
24
+ function workboard(args) {
25
+ const [action, ...params] = args || [];
26
+
27
+ if (!action || action === 'show') return showBoard();
28
+ if (action === 'add') return addItem(params.join(' '));
29
+ if (action === 'move') return moveItem(params[0], params[1]);
30
+ if (action === 'remove') return removeItem(params[0]);
31
+ if (action === 'clear') return clearBoard();
32
+ if (action === 'columns') return manageColumns(params);
33
+ if (action === 'export') return exportBoard();
34
+ if (action === 'import') return importBoard(params[0]);
35
+
36
+ console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
37
+ console.log(chalk.gray(' Kullanım: natureco workboard [show|add|move|remove|clear|columns|export|import]\n'));
38
+ process.exit(1);
39
+ }
40
+
41
+ function showBoard() {
42
+ const board = loadBoard();
43
+ const config = getConfig();
44
+ const wbConfig = config.workboard || {};
45
+ const columns = wbConfig.columns || board.columns;
46
+
47
+ console.log(chalk.cyan('\n 📋 Work Board\n'));
48
+ console.log(chalk.gray(' ' + '─'.repeat(64)));
49
+
50
+ for (const col of columns) {
51
+ const items = board.items.filter(i => i.column === col || (!i.column && col === columns[0]));
52
+ const colLabel = col === 'todo' ? '📋 To Do' : col === 'in-progress' ? '🔄 In Progress' : col === 'done' ? '✅ Done' : col;
53
+ console.log(chalk.white(`\n ${colLabel} (${items.length})`));
54
+
55
+ if (items.length === 0) {
56
+ console.log(chalk.gray(' (empty)'));
57
+ } else {
58
+ for (const item of items) {
59
+ const id = item.id || '';
60
+ const text = item.text || '';
61
+ const priority = item.priority || '';
62
+ const prioTag = priority === 'high' ? chalk.red('‼️') : priority === 'medium' ? chalk.yellow('❗') : priority === 'low' ? chalk.gray('📌') : '';
63
+ console.log(` ${chalk.gray(`[${id}]`)} ${prioTag} ${chalk.white(text.substring(0, 60))}${text.length > 60 ? '…' : ''}`);
64
+ }
65
+ }
66
+ }
67
+
68
+ console.log(chalk.gray('\n ' + '─'.repeat(64)));
69
+ console.log(chalk.gray(` ${board.items.length} total items across ${columns.length} columns`));
70
+ console.log(chalk.gray('\n Commands:'));
71
+ console.log(chalk.cyan(' add <text>') + chalk.gray(' Add item to To Do'));
72
+ console.log(chalk.cyan(' move <id> <column>') + chalk.gray(' Move item to column'));
73
+ console.log(chalk.cyan(' remove <id>') + chalk.gray(' Remove item'));
74
+ console.log(chalk.cyan(' columns list/set') + chalk.gray(' Manage columns'));
75
+ console.log(chalk.cyan(' export') + chalk.gray(' Export board as JSON'));
76
+ console.log(chalk.cyan(' import <file>') + chalk.gray(' Import board from JSON'));
77
+ console.log(chalk.cyan(' clear') + chalk.gray(' Clear all items'));
78
+ console.log();
79
+ }
80
+
81
+ function addItem(text) {
82
+ if (!text) {
83
+ console.log(chalk.red('\n ❌ Text gerekli\n'));
84
+ console.log(chalk.cyan(' natureco workboard add "Task description"\n'));
85
+ process.exit(1);
86
+ }
87
+
88
+ const board = loadBoard();
89
+ const id = Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
90
+ board.items.push({
91
+ id,
92
+ text,
93
+ column: board.columns[0] || 'todo',
94
+ priority: 'medium',
95
+ created: new Date().toISOString()
96
+ });
97
+ saveBoard(board);
98
+ console.log(chalk.green(`\n ✅ Added: ${chalk.white(text)} ${chalk.gray(`[${id}]`)}\n`));
99
+ }
100
+
101
+ function moveItem(id, column) {
102
+ if (!id || !column) {
103
+ console.log(chalk.red('\n ❌ id ve column gerekli\n'));
104
+ console.log(chalk.cyan(' natureco workboard move <id> done\n'));
105
+ process.exit(1);
106
+ }
107
+
108
+ const board = loadBoard();
109
+ const item = board.items.find(i => i.id === id);
110
+ if (!item) {
111
+ console.log(chalk.red(`\n ❌ Item bulunamadı: ${id}\n`));
112
+ process.exit(1);
113
+ }
114
+
115
+ item.column = column;
116
+ item.updated = new Date().toISOString();
117
+ saveBoard(board);
118
+ console.log(chalk.green(`\n ✅ Moved "${item.text.substring(0, 40)}" → ${column}\n`));
119
+ }
120
+
121
+ function removeItem(id) {
122
+ if (!id) {
123
+ console.log(chalk.red('\n ❌ id gerekli\n'));
124
+ process.exit(1);
125
+ }
126
+
127
+ const board = loadBoard();
128
+ const idx = board.items.findIndex(i => i.id === id);
129
+ if (idx === -1) {
130
+ console.log(chalk.red(`\n ❌ Item bulunamadı: ${id}\n`));
131
+ process.exit(1);
132
+ }
133
+
134
+ const removed = board.items.splice(idx, 1)[0];
135
+ saveBoard(board);
136
+ console.log(chalk.green(`\n ✅ Removed: ${removed.text.substring(0, 40)}\n`));
137
+ }
138
+
139
+ function clearBoard() {
140
+ const board = loadBoard();
141
+ board.items = [];
142
+ saveBoard(board);
143
+ console.log(chalk.gray('\n 🗑️ Board temizlendi\n'));
144
+ }
145
+
146
+ function manageColumns(params) {
147
+ const config = getConfig();
148
+ if (!config.workboard) config.workboard = {};
149
+
150
+ if (params[0] === 'set') {
151
+ const cols = params.slice(1);
152
+ if (cols.length === 0) {
153
+ console.log(chalk.red('\n ❌ En az bir kolon gerekli\n'));
154
+ process.exit(1);
155
+ }
156
+ config.workboard.columns = cols;
157
+ saveConfig(config);
158
+ console.log(chalk.green(`\n ✅ Columns set: ${cols.join(', ')}\n`));
159
+ return;
160
+ }
161
+
162
+ const board = loadBoard();
163
+ const columns = config.workboard.columns || board.columns;
164
+ console.log(chalk.cyan('\n 📋 Columns\n'));
165
+ for (const col of columns) {
166
+ const count = board.items.filter(i => i.column === col).length;
167
+ console.log(` ${chalk.white(col)} ${chalk.gray(`(${count} items)`)}`);
168
+ }
169
+ console.log(chalk.gray('\n Set columns:'));
170
+ console.log(chalk.cyan(' natureco workboard columns set todo in-progress review done'));
171
+ console.log();
172
+ }
173
+
174
+ function exportBoard() {
175
+ const board = loadBoard();
176
+ console.log(chalk.cyan('\n 📋 Board Export\n'));
177
+ console.log(JSON.stringify(board, null, 2));
178
+ console.log();
179
+ }
180
+
181
+ function importBoard(filePath) {
182
+ if (!filePath) {
183
+ console.log(chalk.red('\n ❌ Dosya gerekli\n'));
184
+ process.exit(1);
185
+ }
186
+
187
+ if (!fs.existsSync(filePath)) {
188
+ console.log(chalk.red(`\n ❌ Dosya bulunamadı: ${filePath}\n`));
189
+ process.exit(1);
190
+ }
191
+
192
+ try {
193
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
194
+ if (!data.items || !Array.isArray(data.items)) {
195
+ console.log(chalk.red('\n ❌ Geçersiz board JSON (items array gerekli)\n'));
196
+ process.exit(1);
197
+ }
198
+
199
+ saveBoard(data);
200
+ console.log(chalk.green(`\n ✅ Board imported: ${data.items.length} items\n`));
201
+ } catch (err) {
202
+ console.log(chalk.red(`\n ❌ Import hatası: ${err.message}\n`));
203
+ process.exit(1);
204
+ }
205
+ }
206
+
207
+ module.exports = workboard;
@@ -0,0 +1,154 @@
1
+ const { getConfig } = require('../utils/config');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ async function transcribeWithWhisper(audioPath, apiKey) {
6
+ const formData = new FormData();
7
+ const file = await fs.promises.readFile(audioPath);
8
+ const blob = new Blob([file]);
9
+ formData.append('file', blob, path.basename(audioPath));
10
+ formData.append('model', 'whisper-1');
11
+
12
+ const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
13
+ method: 'POST',
14
+ headers: { 'Authorization': `Bearer ${apiKey}` },
15
+ body: formData
16
+ });
17
+
18
+ if (!response.ok) throw new Error(`Whisper error ${response.status}: ${await response.text()}`);
19
+ const data = await response.json();
20
+ return data.text;
21
+ }
22
+
23
+ async function transcribeWithDeepgram(audioPath, apiKey) {
24
+ const file = await fs.promises.readFile(audioPath);
25
+ const response = await fetch('https://api.deepgram.com/v1/listen', {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Authorization': `Token ${apiKey}`,
29
+ 'Content-Type': 'audio/wav'
30
+ },
31
+ body: file
32
+ });
33
+
34
+ if (!response.ok) throw new Error(`Deepgram error ${response.status}: ${await response.text()}`);
35
+ const data = await response.json();
36
+ return data.results?.channels?.[0]?.alternatives?.[0]?.transcript || '';
37
+ }
38
+
39
+ async function analyzeAudio(text, provider, apiKey, analysisPrompt) {
40
+ const baseUrl = provider === 'openai' ? 'https://api.openai.com/v1' : `https://api.${provider}.com/v1`;
41
+
42
+ const prompt = analysisPrompt || 'Analyze this audio transcript. Identify speakers, extract key points, detect sentiment, and summarize.';
43
+
44
+ const response = await fetch(`${baseUrl}/chat/completions`, {
45
+ method: 'POST',
46
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
47
+ body: JSON.stringify({
48
+ model: provider === 'openai' ? 'gpt-4o' : 'claude-3-haiku-20240307',
49
+ messages: [
50
+ { role: 'system', content: prompt },
51
+ { role: 'user', content: `Transcript:\n\n${text}` }
52
+ ],
53
+ max_tokens: 4096
54
+ })
55
+ });
56
+
57
+ if (!response.ok) throw new Error(`Analysis error ${response.status}: ${await response.text()}`);
58
+ return (await response.json()).choices?.[0]?.message?.content || '';
59
+ }
60
+
61
+ module.exports = {
62
+ name: 'audio_understanding',
63
+ description: 'Analyze audio files — transcribe speech and extract insights using AI (OpenAI Whisper + LLM, Deepgram)',
64
+ inputSchema: {
65
+ type: 'object',
66
+ properties: {
67
+ audioPath: { type: 'string', description: 'Path to audio file (mp3, wav, m4a, ogg)' },
68
+ audioUrl: { type: 'string', description: 'URL to audio file (alternative to audioPath)' },
69
+ action: { type: 'string', description: 'Action: transcribe, analyze, full', enum: ['transcribe', 'analyze', 'full'], default: 'full' },
70
+ transcriptionProvider: { type: 'string', description: 'Transcription provider: openai, deepgram (default: openai)', enum: ['openai', 'deepgram'] },
71
+ analysisProvider: { type: 'string', description: 'Analysis provider: openai, anthropic (default: openai)', enum: ['openai', 'anthropic'] },
72
+ analysisPrompt: { type: 'string', description: 'Custom analysis prompt' },
73
+ language: { type: 'string', description: 'Language code (e.g. tr, en, de)' }
74
+ },
75
+ required: ['action']
76
+ },
77
+
78
+ async execute(params) {
79
+ try {
80
+ const config = getConfig();
81
+ let audioPath = params.audioPath;
82
+
83
+ if (params.audioUrl && !audioPath) {
84
+ const tmpDir = require('os').tmpdir();
85
+ const tmpFile = path.join(tmpDir, `audio_${Date.now()}.tmp`);
86
+ const response = await fetch(params.audioUrl);
87
+ if (!response.ok) throw new Error(`Failed to download audio: ${response.status}`);
88
+ const buffer = Buffer.from(await response.arrayBuffer());
89
+ await fs.promises.writeFile(tmpFile, buffer);
90
+ audioPath = tmpFile;
91
+ }
92
+
93
+ if (!audioPath) {
94
+ return { success: false, error: 'audioPath veya audioUrl gerekli' };
95
+ }
96
+
97
+ if (!fs.existsSync(audioPath)) {
98
+ return { success: false, error: `Dosya bulunamadı: ${audioPath}` };
99
+ }
100
+
101
+ const transcribeProv = params.transcriptionProvider || 'openai';
102
+ let transcribeKey;
103
+
104
+ if (transcribeProv === 'openai') {
105
+ transcribeKey = config.openaiApiKey || process.env.OPENAI_API_KEY;
106
+ } else if (transcribeProv === 'deepgram') {
107
+ transcribeKey = config.deepgramApiKey || process.env.DEEPGRAM_API_KEY;
108
+ }
109
+
110
+ if (!transcribeKey) {
111
+ return { success: false, error: `${transcribeProv} API key gerekli` };
112
+ }
113
+
114
+ const result = { success: true, action: params.action || 'full' };
115
+
116
+ if (params.action === 'transcribe' || params.action === 'full' || !params.action) {
117
+ const transcript = transcribeProv === 'deepgram'
118
+ ? await transcribeWithDeepgram(audioPath, transcribeKey)
119
+ : await transcribeWithWhisper(audioPath, transcribeKey);
120
+
121
+ result.transcript = transcript;
122
+ result.transcriptionProvider = transcribeProv;
123
+ }
124
+
125
+ if (params.action === 'analyze' || params.action === 'full') {
126
+ const transcriptForAnalysis = result.transcript || (transcribeProv === 'deepgram'
127
+ ? await transcribeWithDeepgram(audioPath, transcribeKey)
128
+ : await transcribeWithWhisper(audioPath, transcribeKey));
129
+
130
+ const analysisProv = params.analysisProvider || 'openai';
131
+ const analysisApiKey = analysisProv === 'openai'
132
+ ? (config.openaiApiKey || process.env.OPENAI_API_KEY)
133
+ : (config.anthropicApiKey || process.env.ANTHROPIC_API_KEY);
134
+
135
+ if (!analysisApiKey) {
136
+ return { success: false, error: `${analysisProv} API key gerekli` };
137
+ }
138
+
139
+ const analysis = await analyzeAudio(transcriptForAnalysis, analysisProv, analysisApiKey, params.analysisPrompt);
140
+ result.analysis = analysis;
141
+ result.analysisProvider = analysisProv;
142
+ result.transcript = transcriptForAnalysis;
143
+ }
144
+
145
+ if (audioPath !== params.audioPath && audioPath) {
146
+ await fs.promises.unlink(audioPath).catch(() => {});
147
+ }
148
+
149
+ return result;
150
+ } catch (error) {
151
+ return { success: false, error: error.message };
152
+ }
153
+ }
154
+ };
@@ -0,0 +1,112 @@
1
+ const { getConfig } = require('../utils/config');
2
+
3
+ async function tryPlaywright(action, params) {
4
+ try {
5
+ const { chromium } = require('playwright');
6
+ const browser = await chromium.launch({ headless: true });
7
+ try {
8
+ const page = await browser.newPage();
9
+
10
+ if (action === 'open') {
11
+ await page.goto(params.url, { waitUntil: 'networkidle', timeout: 30000 });
12
+ const title = await page.title();
13
+ const content = await page.evaluate(() => document.body.innerText);
14
+ return { success: true, action, title, url: params.url, content: content.substring(0, 10000) };
15
+ }
16
+
17
+ if (action === 'screenshot') {
18
+ await page.goto(params.url, { waitUntil: 'networkidle', timeout: 30000 });
19
+ const screenshot = await page.screenshot({ type: 'png', fullPage: params.fullPage !== false });
20
+ return { success: true, action, url: params.url, screenshot: screenshot.toString('base64'), format: 'png' };
21
+ }
22
+
23
+ if (action === 'evaluate') {
24
+ await page.goto(params.url, { waitUntil: 'networkidle', timeout: 30000 });
25
+ const result = await page.evaluate(params.script);
26
+ return { success: true, action, url: params.url, result: JSON.stringify(result) };
27
+ }
28
+
29
+ if (action === 'html') {
30
+ await page.goto(params.url, { waitUntil: 'networkidle', timeout: 30000 });
31
+ const html = await page.content();
32
+ return { success: true, action, url: params.url, html: html.substring(0, 50000) };
33
+ }
34
+
35
+ return { success: false, error: `Unknown browser action: ${action}` };
36
+ } finally {
37
+ await browser.close();
38
+ }
39
+ } catch (err) {
40
+ if (err.code === 'MODULE_NOT_FOUND' || err.message?.includes('Cannot find module')) {
41
+ return { success: false, error: 'Playwright kurulu değil. Yüklemek için: npm install playwright', fallback: true };
42
+ }
43
+ return { success: false, error: err.message };
44
+ }
45
+ }
46
+
47
+ async function httpFallback(url) {
48
+ try {
49
+ const https = require('https');
50
+ const http = require('http');
51
+ const transport = url.startsWith('https') ? https : http;
52
+
53
+ return new Promise((resolve) => {
54
+ const req = transport.get(url, { headers: { 'User-Agent': 'NatureCo-CLI/2.0' }, timeout: 15000 }, (res) => {
55
+ let data = '';
56
+ res.on('data', (chunk) => { data += chunk; });
57
+ res.on('end', () => {
58
+ const title = data.match(/<title>([^<]*)<\/title>/i)?.[1]?.trim() || '';
59
+ const text = data.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
60
+ resolve({ success: true, action: 'open', title, url, content: text.substring(0, 10000), mode: 'http-fallback' });
61
+ });
62
+ });
63
+ req.on('error', (err) => resolve({ success: false, error: err.message }));
64
+ req.on('timeout', () => { req.destroy(); resolve({ success: false, error: 'timeout' }); });
65
+ });
66
+ } catch (err) {
67
+ return { success: false, error: err.message };
68
+ }
69
+ }
70
+
71
+ module.exports = {
72
+ name: 'browser',
73
+ description: 'Headless browser automation — open URLs, take screenshots, evaluate JS, get HTML. Uses Playwright if available, falls back to HTTP.',
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ action: { type: 'string', description: 'Action: open, screenshot, evaluate, html', enum: ['open', 'screenshot', 'evaluate', 'html'] },
78
+ url: { type: 'string', description: 'URL to navigate to' },
79
+ script: { type: 'string', description: 'JavaScript to evaluate (for evaluate action)' },
80
+ fullPage: { type: 'boolean', description: 'Full page screenshot (default: true)' }
81
+ },
82
+ required: ['action', 'url']
83
+ },
84
+
85
+ async execute(params) {
86
+ if (params.action === 'open') {
87
+ const pw = await tryPlaywright('open', params);
88
+ if (pw.fallback) return httpFallback(params.url);
89
+ return pw;
90
+ }
91
+
92
+ if (params.action === 'screenshot') {
93
+ const pw = await tryPlaywright('screenshot', params);
94
+ if (pw.fallback) return { success: false, error: 'Screenshot için Playwright gerekli. Kur: npm install playwright' };
95
+ return pw;
96
+ }
97
+
98
+ if (params.action === 'evaluate') {
99
+ const pw = await tryPlaywright('evaluate', params);
100
+ if (pw.fallback) return { success: false, error: 'JS evaluate için Playwright gerekli. Kur: npm install playwright' };
101
+ return pw;
102
+ }
103
+
104
+ if (params.action === 'html') {
105
+ const pw = await tryPlaywright('html', params);
106
+ if (pw.fallback) return httpFallback(params.url);
107
+ return pw;
108
+ }
109
+
110
+ return { success: false, error: `Unknown action: ${params.action}` };
111
+ }
112
+ };
@@ -0,0 +1,104 @@
1
+ module.exports = {
2
+ name: 'canvas',
3
+ description: 'Create and display rich content: tables, charts, formatted text, and structured data',
4
+ inputSchema: {
5
+ type: 'object',
6
+ properties: {
7
+ type: { type: 'string', description: 'Canvas type: table, markdown, code, data, separator, heading', enum: ['table', 'markdown', 'code', 'data', 'separator', 'heading'] },
8
+ title: { type: 'string', description: 'Section title' },
9
+ content: { type: 'string', description: 'Text content for markdown/code types' },
10
+ headers: { type: 'array', items: { type: 'string' }, description: 'Column headers (for table type)' },
11
+ rows: { type: 'array', items: { type: 'array', items: { type: 'string' } }, description: 'Table rows (for table type)' },
12
+ data: { type: 'object', description: 'Structured data to display (for data type)' },
13
+ language: { type: 'string', description: 'Code language (for code type)' }
14
+ },
15
+ required: ['type']
16
+ },
17
+
18
+ async execute(params) {
19
+ try {
20
+ const chalk = require('chalk');
21
+ const w = process.stdout.columns || 80;
22
+ const line = chalk.gray('─'.repeat(w));
23
+ const thin = chalk.gray('─'.repeat(40));
24
+
25
+ let output = ['', line];
26
+
27
+ if (params.title) {
28
+ output.push(chalk.bold.cyan(` ${params.title}`));
29
+ output.push(thin);
30
+ }
31
+
32
+ if (params.type === 'heading') {
33
+ output = ['', '', chalk.bold.green(` ${'█'.repeat(4)} ${params.content}`), ''];
34
+ output.push(line);
35
+ }
36
+
37
+ if (params.type === 'separator') {
38
+ output = ['', line, ''];
39
+ }
40
+
41
+ if (params.type === 'markdown') {
42
+ const lines = (params.content || '').split('\n');
43
+ for (const lineText of lines) {
44
+ output.push(lineText.startsWith('#')
45
+ ? chalk.bold.cyan(` ${lineText}`)
46
+ : lineText.startsWith('-') || lineText.startsWith('*')
47
+ ? chalk.white(` ${lineText}`)
48
+ : lineText.startsWith('>')
49
+ ? chalk.gray(` ${lineText}`)
50
+ : chalk.white(` ${lineText}`));
51
+ }
52
+ }
53
+
54
+ if (params.type === 'code') {
55
+ const lang = params.language || 'text';
56
+ output.push(chalk.gray(` ── ${lang} ──`));
57
+ const codeLines = (params.content || '').split('\n');
58
+ for (const lineText of codeLines) {
59
+ output.push(chalk.yellow(` │ ${lineText}`));
60
+ }
61
+ output.push(chalk.gray(' ' + '─'.repeat(40)));
62
+ }
63
+
64
+ if (params.type === 'table') {
65
+ const headers = params.headers || [];
66
+ const rows = params.rows || [];
67
+
68
+ if (headers.length > 0) {
69
+ const headerLine = headers.map(h => chalk.bold.cyan(h.padEnd(20))).join(' │ ');
70
+ output.push(` ${headerLine}`);
71
+ output.push(chalk.gray(` ${'─'.repeat(Math.max(40, headers.length * 22))}`));
72
+ }
73
+
74
+ for (const row of rows) {
75
+ const rowLine = row.map((cell, i) => {
76
+ const width = headers[i] ? headers[i].length : 20;
77
+ return (cell || '').padEnd(Math.max(width, 20));
78
+ }).join(' │ ');
79
+ output.push(` ${rowLine}`);
80
+ }
81
+ }
82
+
83
+ if (params.type === 'data') {
84
+ const d = params.data || {};
85
+ for (const [key, value] of Object.entries(d)) {
86
+ const val = typeof value === 'object' ? JSON.stringify(value) : String(value);
87
+ output.push(chalk.white(` ${key}`) + chalk.gray(': ') + chalk.cyan(val.length > 60 ? val.slice(0, 60) + '...' : val));
88
+ }
89
+ }
90
+
91
+ output.push(line);
92
+ output.push('');
93
+
94
+ return {
95
+ success: true,
96
+ type: params.type,
97
+ output: output.join('\n'),
98
+ rendered: true
99
+ };
100
+ } catch (error) {
101
+ return { success: false, error: error.message };
102
+ }
103
+ }
104
+ };
@@ -0,0 +1,84 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+
5
+ module.exports = {
6
+ name: 'document_extract',
7
+ description: 'Extract text content from local documents (PDF, DOCX, TXT, CSV, JSON, MD, RTF, ODT)',
8
+ inputSchema: {
9
+ type: 'object',
10
+ properties: {
11
+ path: { type: 'string', description: 'File path to extract text from' },
12
+ maxChars: { type: 'number', description: 'Maximum characters to return (default: 50000)', default: 50000 }
13
+ },
14
+ required: ['path']
15
+ },
16
+
17
+ async execute(params) {
18
+ try {
19
+ const filePath = path.resolve(params.path.replace(/^~/, require('os').homedir()));
20
+ const maxChars = params.maxChars || 50000;
21
+
22
+ if (!fs.existsSync(filePath)) {
23
+ return { success: false, error: `Dosya bulunamadı: ${filePath}` };
24
+ }
25
+
26
+ const stats = fs.statSync(filePath);
27
+ if (stats.size > 50 * 1024 * 1024) {
28
+ return { success: false, error: 'Dosya çok büyük (max 50MB)' };
29
+ }
30
+
31
+ const ext = path.extname(filePath).toLowerCase();
32
+ let text = '';
33
+
34
+ if (ext === '.txt' || ext === '.csv' || ext === '.json' || ext === '.md' || ext === '.xml' || ext === '.yaml' || ext === '.yml') {
35
+ text = fs.readFileSync(filePath, 'utf-8');
36
+ } else if (ext === '.pdf') {
37
+ try {
38
+ text = execSync(`pdftotext "${filePath}" -`, { encoding: 'utf-8', timeout: 30000, stdio: ['pipe', 'pipe', 'ignore'] });
39
+ } catch {
40
+ // Fallback: read raw PDF extract
41
+ const raw = fs.readFileSync(filePath, 'utf-8');
42
+ const matches = raw.match(/\((.*?)\)/g);
43
+ text = (matches || []).map(m => m.slice(1, -1)).filter(s => s.length > 3).join(' ');
44
+ }
45
+ } else if (ext === '.docx') {
46
+ try {
47
+ const zip = require('../utils/zip-reader');
48
+ text = await zip.readText(filePath);
49
+ } catch {
50
+ return { success: false, error: 'DOCX okuma hatası. npm install adm-zip gerekli olabilir.' };
51
+ }
52
+ } else if (ext === '.rtf' || ext === '.odt') {
53
+ const raw = fs.readFileSync(filePath, 'utf-8');
54
+ text = raw.replace(/<[^>]+>/g, ' ').replace(/\\[a-z]+|\{[^}]*\}|[{}]/g, ' ').replace(/\s+/g, ' ').trim();
55
+ } else {
56
+ // Fallback: read as text
57
+ text = fs.readFileSync(filePath, 'utf-8');
58
+ }
59
+
60
+ const cleaned = text
61
+ .replace(/\r\n/g, '\n')
62
+ .replace(/\x00/g, '')
63
+ .trim();
64
+
65
+ const truncated = cleaned.length > maxChars
66
+ ? cleaned.slice(0, maxChars) + '...'
67
+ : cleaned;
68
+
69
+ return {
70
+ success: true,
71
+ path: filePath,
72
+ extension: ext,
73
+ fileSize: stats.size,
74
+ content: truncated,
75
+ wordCount: cleaned.split(/\s+/).length,
76
+ totalChars: cleaned.length,
77
+ truncated: cleaned.length > maxChars,
78
+ source: 'document_extract'
79
+ };
80
+ } catch (error) {
81
+ return { success: false, error: error.message };
82
+ }
83
+ }
84
+ };