natureco-cli 5.20.4 → 5.21.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.
@@ -1,324 +1,324 @@
1
- /**
2
- * NatureCo CLI — Tool Definitions for OpenAI-compatible APIs
3
- *
4
- * src/tools/*.js dosyalarını OpenAI uyumlu function calling format'ına dönüştürür.
5
- * v5.7.17: Emoji + toolset + check_fn + registry entegrasyonu.
6
- */
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
- const { globalRegistry } = require('./registry');
11
- const sandbox = require('./sandbox');
12
-
13
- // Lazy config read — avoids circular deps
14
- function _getSandboxLevel() {
15
- try { return sandbox.getLevel(require('./config').getConfig()); } catch { return 'none'; }
16
- }
17
-
18
- const TOOLS_DIR = path.join(__dirname, '..', 'tools');
19
-
20
- // ── Emoji map (central, tek kaynak) ──────────────────────────────────────
21
- const EMOJI_MAP = {
22
- // File operations
23
- read_file: '📖', write_file: '✏️', edit_file: '🖊️', list_dir: '📂', file_search: '🔍', grep_search: '🔎', filesystem: '🗄️',
24
- // Terminal
25
- bash: '💻', shell_command: '⌨️',
26
- // Web
27
- duckduckgo: '🦆', duckduckgo_search: '🦆', web_search: '🌐', web_readability: '📄', firecrawl: '🔥', searxng: '🔬', searxng_search: '🔬', http_request: '🌍', http: '🌍', exa_search: '🔬', parallel_search: '⚡',
28
- // Browser
29
- browser: '🖥️', browser_use: '🌐',
30
- // Memory
31
- memory: '🧠', memory_write: '🧠', memory_search: '🔍',
32
- // Skills
33
- skill_view: '📚', skills_list: '📋', skill_generate: '✨', skills_autoload: '🔄', skills_marketplace: '🏪', skill_manage: '🛠️', skills_download: '📥',
34
- // Agent
35
- delegate_task: '👥', llm_task: '🤖', sub_agent: '👤',
36
- // Documents
37
- document_extract: '📄', notebook_edit: '📓', notes_add: '📝',
38
- // Git
39
- git: '🔀',
40
- // Plan / Todo
41
- plan: '📋', todo_write: '✅',
42
- // Media
43
- image_generation: '🎨', video_generation: '🎬', music_generation: '🎵', media_understanding: '📺', text_to_speech: '🔊', speech_to_text: '🎤', voice_chat: '🗣️',
44
- // macOS
45
- mac_alarm: '⏰', mac_app_open: '🚀', mac_app_quit: '⏹️', mac_notify: '🔔', macos_screenshot: '📸', phone_control: '📱', phone_control_enhanced: '📱',
46
- // Calendar
47
- calendar_add: '📅',
48
- // Reminder
49
- reminder_add: '⏰',
50
- // Dashboard
51
- dashboard: '📊',
52
- kanban: '📋',
53
- // Canvas
54
- canvas: '🎨',
55
- // Plugin
56
- plugin: '🔌',
57
- // Soul
58
- soul: '💫',
59
- // Cron
60
- cron_create: '⏱️',
61
- // Thread
62
- thread_ownership: '🔗',
63
- // Audio understanding
64
- audio_understanding: '🎵',
65
- // Code execution
66
- code_execution: '⚡',
67
- // Cross-session
68
- cross_session_memory: '🔗',
69
- // v5.10.0: New tools
70
- url_safety: '🛡️', approval: '✅', checkpoint: '💾', file_state: '🔍',
71
- pii_redact: '🔒', clarify: '❓', session_search: '🔎', x_search: '🐦',
72
- discord: '💬', send_message: '📨', async_delegation: '⏳', blueprint: '📐',
73
- spotify: '🎧', homeassistant: '🏠', microsoft_graph: '📊', computer_use: '🖱️', computer_use_loop: '🔄',
74
- google_meet: '📹',
75
- // Orchestrator
76
- workflow: '⚙️',
77
- social_open: '🔗', youtube_ac: '🎬', memory_provider: '🗄️',
78
- };
79
-
80
- // ── Toolset grouping ─────────────────────────────────────────────────────
81
- const TOOLSET_MAP = {
82
- // File
83
- read_file: 'file', write_file: 'file', edit_file: 'file', list_dir: 'file',
84
- file_search: 'file', grep_search: 'file', filesystem: 'file',
85
- // Terminal
86
- bash: 'terminal', shell_command: 'terminal',
87
- // Web
88
- duckduckgo: 'web', web_search: 'web', web_readability: 'web', firecrawl: 'web',
89
- searxng: 'web', http_request: 'web', http: 'web', exa_search: 'web',
90
- parallel_search: 'web',
91
- duckduckgo_search: 'web', searxng_search: 'web',
92
- // Browser
93
- browser: 'browser', browser_use: 'browser',
94
- // Memory
95
- memory: 'memory', memory_write: 'memory', memory_search: 'memory',
96
- // Skills
97
- skill_view: 'skills', skills_list: 'skills', skill_generate: 'skills',
98
- skills_autoload: 'skills', skills_marketplace: 'skills', skill_manage: 'skills', skills_download: 'skills',
99
- // Agent
100
- delegate_task: 'agent', llm_task: 'agent',
101
- // Documents
102
- document_extract: 'documents', notebook_edit: 'documents', notes_add: 'documents',
103
- // Git
104
- git: 'git',
105
- // Plan / Todo
106
- plan: 'planning', todo_write: 'planning',
107
- // Media
108
- image_generation: 'media', video_generation: 'media', music_generation: 'media',
109
- media_understanding: 'media', text_to_speech: 'media', speech_to_text: 'media',
110
- voice_chat: 'media', audio_understanding: 'media',
111
- // macOS
112
- mac_alarm: 'macos', mac_app_open: 'macos', mac_app_quit: 'macos', mac_notify: 'macos',
113
- macos_screenshot: 'macos', phone_control: 'macos', phone_control_enhanced: 'macos',
114
- // Calendar
115
- calendar_add: 'calendar',
116
- // Reminder
117
- reminder_add: 'reminders',
118
- // Other
119
- dashboard: 'dashboard', canvas: 'canvas', plugin: 'plugins', soul: 'soul',
120
- kanban: 'planning',
121
- cron_create: 'cron', thread_ownership: 'threads', code_execution: 'sandbox',
122
- cross_session_memory: 'memory',
123
- // v5.10.0: New tools
124
- url_safety: 'security', approval: 'security', pii_redact: 'security',
125
- checkpoint: 'system', file_state: 'system',
126
- clarify: 'agent',
127
- session_search: 'memory',
128
- x_search: 'web', discord: 'communication', send_message: 'communication',
129
- async_delegation: 'agent', blueprint: 'planning', workflow: 'orchestrator',
130
- spotify: 'media', homeassistant: 'iot', microsoft_graph: 'office',
131
- computer_use: 'automation', computer_use_loop: 'automation', google_meet: 'communication',
132
- social_open: 'communication', youtube_ac: 'media',
133
- sub_agent: 'agent', memory_provider: 'memory',
134
- };
135
-
136
- // ── check_fn'ler (tool availability kontrolleri) ────────────────────────
137
- function _checkBrowser() {
138
- try {
139
- require.resolve('playwright');
140
- return true;
141
- } catch { return false; }
142
- }
143
-
144
- function _checkDuckDuckGo() {
145
- return true; // API-based, always available
146
- }
147
-
148
- function _checkMacOSTools() {
149
- return process.platform === 'darwin';
150
- }
151
-
152
- const CHECK_FN_MAP = {
153
- browser: _checkBrowser,
154
- mac_alarm: _checkMacOSTools,
155
- mac_app_open: _checkMacOSTools,
156
- mac_app_quit: _checkMacOSTools,
157
- mac_notify: _checkMacOSTools,
158
- macos_screenshot: _checkMacOSTools,
159
- phone_control: _checkMacOSTools,
160
- phone_control_enhanced: _checkMacOSTools,
161
- // v5.10.0: google_meet create macOS-only; computer_use partial cross-platform
162
- google_meet: () => process.platform === 'darwin' || true, // only 'create' is macOS-only, 'open' cross-platform
163
- };
164
-
165
- // ── Provider filtering ───────────────────────────────────────────────────
166
- function getToolsForProvider(allTools, providerUrl) {
167
- const url = (providerUrl || '').toLowerCase();
168
- if (url.includes('groq.com')) {
169
- const allowed = ['read_file', 'write_file', 'bash', 'shell_command', 'list_dir', 'soul', 'memory', 'memory_write', 'memory_search', 'filesystem', 'grep_search'];
170
- return allTools.filter(t => allowed.includes(t.name));
171
- }
172
- return allTools;
173
- }
174
-
175
- function loadToolDefinitions() {
176
- const tools = [];
177
- const files = fs.readdirSync(TOOLS_DIR).filter(f => f.endsWith('.js'));
178
-
179
- let isGroq = false;
180
- try {
181
- const { getConfig } = require('./config');
182
- const cfg = getConfig();
183
- if (cfg.providerUrl && cfg.providerUrl.toLowerCase().includes('groq.com')) {
184
- isGroq = true;
185
- }
186
- } catch (e) {}
187
-
188
- let disabledTools = new Set();
189
- try {
190
- const { getConfig } = require('./config');
191
- const cfg = getConfig();
192
- if (Array.isArray(cfg.disabledTools)) disabledTools = new Set(cfg.disabledTools);
193
- } catch (e) {}
194
-
195
- const GROQ_ALLOWED = new Set([
196
- 'read_file', 'write_file', 'list_dir', 'bash', 'shell_command',
197
- 'soul', 'memory', 'memory_write', 'memory_search', 'filesystem', 'grep_search'
198
- ]);
199
-
200
- for (const file of files) {
201
- try {
202
- const toolPath = path.join(TOOLS_DIR, file);
203
- const mod = require(toolPath);
204
- const toolName = mod.name || path.basename(file, '.js');
205
-
206
- if (isGroq && !GROQ_ALLOWED.has(toolName)) continue;
207
- if (disabledTools.has(toolName)) continue;
208
-
209
- const meta = {
210
- name: toolName,
211
- description: mod.description || `${path.basename(file, '.js')} tool`,
212
- parameters: mod.parameters || mod.inputSchema || { type: 'object', properties: {} },
213
- execute: mod.execute || (mod.default && mod.default.execute) || null,
214
- emoji: EMOJI_MAP[toolName] || '',
215
- toolset: TOOLSET_MAP[toolName] || 'general',
216
- checkFn: CHECK_FN_MAP[toolName] || null,
217
- };
218
-
219
- if (meta.execute) {
220
- tools.push(meta);
221
- // Registry'ye kaydet
222
- globalRegistry.register({
223
- name: meta.name,
224
- toolset: meta.toolset,
225
- schema: { name: meta.name, description: meta.description, parameters: meta.parameters },
226
- handler: meta.execute,
227
- checkFn: meta.checkFn,
228
- emoji: meta.emoji,
229
- });
230
- }
231
- } catch (e) {
232
- // Sessizce atla
233
- }
234
- }
235
- return tools;
236
- }
237
-
238
- const ALIAS_MAP = {
239
- 'brave_search': 'duckduckgo_search', 'brave-web-search': 'duckduckgo_search',
240
- 'google_search': 'duckduckgo_search', 'web_search': 'duckduckgo_search',
241
- 'browse': 'browser', 'shell': 'bash', 'bash_command': 'bash',
242
- 'execute_command': 'bash', 'run_command': 'bash',
243
- 'http': 'http_request',
244
- };
245
-
246
- const BLOCKED_NAMES = new Set([
247
- 'brave_search', 'brave-web-search', 'google_search', 'web_search',
248
- 'browse', 'open', 'search', 'shell', 'bash_command', 'execute_command',
249
- 'run_command', 'sql', 'query', 'lookup', 'http',
250
- ]);
251
-
252
- // ── check_fn TTL cache (Hermes-style, ~30s) ────────────────────────────
253
- const _checkFnCache = new Map();
254
- function _cachedCheckFn(fn, key) {
255
- const now = Date.now();
256
- const cached = _checkFnCache.get(key);
257
- if (cached && now - cached.ts < 30000) return cached.result;
258
- let result = true;
259
- try { result = fn() !== false; } catch { result = false; }
260
- _checkFnCache.set(key, { result, ts: now });
261
- return result;
262
- }
263
-
264
- function toOpenAIFormat(toolDefs) {
265
- return toolDefs
266
- .filter(t => !BLOCKED_NAMES.has(t.name))
267
- .filter(t => {
268
- if (t.checkFn) {
269
- return _cachedCheckFn(t.checkFn, t.name);
270
- }
271
- return true;
272
- })
273
- .map(t => {
274
- let name = t.name;
275
- if (ALIAS_MAP[name]) name = ALIAS_MAP[name];
276
-
277
- const cleanParams = JSON.parse(JSON.stringify(t.parameters || {}));
278
- if (cleanParams.properties) {
279
- Object.keys(cleanParams.properties).forEach(key => {
280
- const prop = cleanParams.properties[key];
281
- if (Array.isArray(prop.type)) prop.type = prop.type[0];
282
- delete prop.additionalProperties;
283
- });
284
- }
285
-
286
- return {
287
- type: 'function',
288
- function: { name, description: t.description, parameters: cleanParams },
289
- };
290
- });
291
- }
292
-
293
- async function executeTool(toolName, args, toolDefs) {
294
- const tool = toolDefs.find(t => t.name === toolName);
295
- if (!tool) return { error: `Tool bulunamadı: ${toolName}` };
296
- if (!tool.execute) return { error: `Tool execute fonksiyonu yok: ${toolName}` };
297
- // checkFn — tool disabled? (re-check at runtime with cache)
298
- if (tool.checkFn) {
299
- if (!_cachedCheckFn(tool.checkFn, tool.name)) {
300
- return { error: `${toolName} şu anda kullanılamıyor (check_fn engelledi)` };
301
- }
302
- }
303
-
304
- // Sandbox restrictions for bash/shell_command
305
- if (toolName === 'bash' || toolName === 'shell_command') {
306
- const level = _getSandboxLevel();
307
- const cmd = (args && args.command) || '';
308
- if (level === 'strict' && sandbox.isNetworkCommand(cmd)) {
309
- return { error: 'strict sandbox: network komutları engellendi. Daha düşük sandbox seviyesi kullanın.' };
310
- }
311
- }
312
-
313
- try {
314
- const result = await tool.execute(args || {});
315
- return { result };
316
- } catch (e) {
317
- return { error: e.message || String(e) };
318
- }
319
- }
320
-
321
- module.exports = {
322
- loadToolDefinitions, toOpenAIFormat, executeTool, getToolsForProvider,
323
- EMOJI_MAP, TOOLSET_MAP, CHECK_FN_MAP,
324
- };
1
+ /**
2
+ * NatureCo CLI — Tool Definitions for OpenAI-compatible APIs
3
+ *
4
+ * src/tools/*.js dosyalarını OpenAI uyumlu function calling format'ına dönüştürür.
5
+ * v5.7.17: Emoji + toolset + check_fn + registry entegrasyonu.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { globalRegistry } = require('./registry');
11
+ const sandbox = require('./sandbox');
12
+
13
+ // Lazy config read — avoids circular deps
14
+ function _getSandboxLevel() {
15
+ try { return sandbox.getLevel(require('./config').getConfig()); } catch { return 'none'; }
16
+ }
17
+
18
+ const TOOLS_DIR = path.join(__dirname, '..', 'tools');
19
+
20
+ // ── Emoji map (central, tek kaynak) ──────────────────────────────────────
21
+ const EMOJI_MAP = {
22
+ // File operations
23
+ read_file: '📖', write_file: '✏️', edit_file: '🖊️', list_dir: '📂', file_search: '🔍', grep_search: '🔎', filesystem: '🗄️',
24
+ // Terminal
25
+ bash: '💻', shell_command: '⌨️',
26
+ // Web
27
+ duckduckgo: '🦆', duckduckgo_search: '🦆', web_search: '🌐', web_readability: '📄', firecrawl: '🔥', searxng: '🔬', searxng_search: '🔬', http_request: '🌍', http: '🌍', exa_search: '🔬', parallel_search: '⚡',
28
+ // Browser
29
+ browser: '🖥️', browser_use: '🌐',
30
+ // Memory
31
+ memory: '🧠', memory_write: '🧠', memory_search: '🔍',
32
+ // Skills
33
+ skill_view: '📚', skills_list: '📋', skill_generate: '✨', skills_autoload: '🔄', skills_marketplace: '🏪', skill_manage: '🛠️', skills_download: '📥',
34
+ // Agent
35
+ delegate_task: '👥', llm_task: '🤖', sub_agent: '👤',
36
+ // Documents
37
+ document_extract: '📄', notebook_edit: '📓', notes_add: '📝',
38
+ // Git
39
+ git: '🔀',
40
+ // Plan / Todo
41
+ plan: '📋', todo_write: '✅',
42
+ // Media
43
+ image_generation: '🎨', video_generation: '🎬', music_generation: '🎵', media_understanding: '📺', text_to_speech: '🔊', speech_to_text: '🎤', voice_chat: '🗣️',
44
+ // macOS
45
+ mac_alarm: '⏰', mac_app_open: '🚀', mac_app_quit: '⏹️', mac_notify: '🔔', macos_screenshot: '📸', phone_control: '📱', phone_control_enhanced: '📱',
46
+ // Calendar
47
+ calendar_add: '📅',
48
+ // Reminder
49
+ reminder_add: '⏰',
50
+ // Dashboard
51
+ dashboard: '📊',
52
+ kanban: '📋',
53
+ // Canvas
54
+ canvas: '🎨',
55
+ // Plugin
56
+ plugin: '🔌',
57
+ // Soul
58
+ soul: '💫',
59
+ // Cron
60
+ cron_create: '⏱️',
61
+ // Thread
62
+ thread_ownership: '🔗',
63
+ // Audio understanding
64
+ audio_understanding: '🎵',
65
+ // Code execution
66
+ code_execution: '⚡',
67
+ // Cross-session
68
+ cross_session_memory: '🔗',
69
+ // v5.10.0: New tools
70
+ url_safety: '🛡️', approval: '✅', checkpoint: '💾', file_state: '🔍',
71
+ pii_redact: '🔒', clarify: '❓', session_search: '🔎', x_search: '🐦',
72
+ discord: '💬', send_message: '📨', async_delegation: '⏳', blueprint: '📐',
73
+ spotify: '🎧', homeassistant: '🏠', microsoft_graph: '📊', computer_use: '🖱️', computer_use_loop: '🔄',
74
+ google_meet: '📹',
75
+ // Orchestrator
76
+ workflow: '⚙️',
77
+ social_open: '🔗', youtube_ac: '🎬', memory_provider: '🗄️',
78
+ };
79
+
80
+ // ── Toolset grouping ─────────────────────────────────────────────────────
81
+ const TOOLSET_MAP = {
82
+ // File
83
+ read_file: 'file', write_file: 'file', edit_file: 'file', list_dir: 'file',
84
+ file_search: 'file', grep_search: 'file', filesystem: 'file',
85
+ // Terminal
86
+ bash: 'terminal', shell_command: 'terminal',
87
+ // Web
88
+ duckduckgo: 'web', web_search: 'web', web_readability: 'web', firecrawl: 'web',
89
+ searxng: 'web', http_request: 'web', http: 'web', exa_search: 'web',
90
+ parallel_search: 'web',
91
+ duckduckgo_search: 'web', searxng_search: 'web',
92
+ // Browser
93
+ browser: 'browser', browser_use: 'browser',
94
+ // Memory
95
+ memory: 'memory', memory_write: 'memory', memory_search: 'memory',
96
+ // Skills
97
+ skill_view: 'skills', skills_list: 'skills', skill_generate: 'skills',
98
+ skills_autoload: 'skills', skills_marketplace: 'skills', skill_manage: 'skills', skills_download: 'skills',
99
+ // Agent
100
+ delegate_task: 'agent', llm_task: 'agent',
101
+ // Documents
102
+ document_extract: 'documents', notebook_edit: 'documents', notes_add: 'documents',
103
+ // Git
104
+ git: 'git',
105
+ // Plan / Todo
106
+ plan: 'planning', todo_write: 'planning',
107
+ // Media
108
+ image_generation: 'media', video_generation: 'media', music_generation: 'media',
109
+ media_understanding: 'media', text_to_speech: 'media', speech_to_text: 'media',
110
+ voice_chat: 'media', audio_understanding: 'media',
111
+ // macOS
112
+ mac_alarm: 'macos', mac_app_open: 'macos', mac_app_quit: 'macos', mac_notify: 'macos',
113
+ macos_screenshot: 'macos', phone_control: 'macos', phone_control_enhanced: 'macos',
114
+ // Calendar
115
+ calendar_add: 'calendar',
116
+ // Reminder
117
+ reminder_add: 'reminders',
118
+ // Other
119
+ dashboard: 'dashboard', canvas: 'canvas', plugin: 'plugins', soul: 'soul',
120
+ kanban: 'planning',
121
+ cron_create: 'cron', thread_ownership: 'threads', code_execution: 'sandbox',
122
+ cross_session_memory: 'memory',
123
+ // v5.10.0: New tools
124
+ url_safety: 'security', approval: 'security', pii_redact: 'security',
125
+ checkpoint: 'system', file_state: 'system',
126
+ clarify: 'agent',
127
+ session_search: 'memory',
128
+ x_search: 'web', discord: 'communication', send_message: 'communication',
129
+ async_delegation: 'agent', blueprint: 'planning', workflow: 'orchestrator',
130
+ spotify: 'media', homeassistant: 'iot', microsoft_graph: 'office',
131
+ computer_use: 'automation', computer_use_loop: 'automation', google_meet: 'communication',
132
+ social_open: 'communication', youtube_ac: 'media',
133
+ sub_agent: 'agent', memory_provider: 'memory',
134
+ };
135
+
136
+ // ── check_fn'ler (tool availability kontrolleri) ────────────────────────
137
+ function _checkBrowser() {
138
+ try {
139
+ require.resolve('playwright');
140
+ return true;
141
+ } catch { return false; }
142
+ }
143
+
144
+ function _checkDuckDuckGo() {
145
+ return true; // API-based, always available
146
+ }
147
+
148
+ function _checkMacOSTools() {
149
+ return process.platform === 'darwin';
150
+ }
151
+
152
+ const CHECK_FN_MAP = {
153
+ browser: _checkBrowser,
154
+ mac_alarm: _checkMacOSTools,
155
+ mac_app_open: _checkMacOSTools,
156
+ mac_app_quit: _checkMacOSTools,
157
+ mac_notify: _checkMacOSTools,
158
+ macos_screenshot: _checkMacOSTools,
159
+ phone_control: _checkMacOSTools,
160
+ phone_control_enhanced: _checkMacOSTools,
161
+ // v5.10.0: google_meet create macOS-only; computer_use partial cross-platform
162
+ google_meet: () => process.platform === 'darwin' || true, // only 'create' is macOS-only, 'open' cross-platform
163
+ };
164
+
165
+ // ── Provider filtering ───────────────────────────────────────────────────
166
+ function getToolsForProvider(allTools, providerUrl) {
167
+ const url = (providerUrl || '').toLowerCase();
168
+ if (url.includes('groq.com')) {
169
+ const allowed = ['read_file', 'write_file', 'bash', 'shell_command', 'list_dir', 'soul', 'memory', 'memory_write', 'memory_search', 'filesystem', 'grep_search'];
170
+ return allTools.filter(t => allowed.includes(t.name));
171
+ }
172
+ return allTools;
173
+ }
174
+
175
+ function loadToolDefinitions() {
176
+ const tools = [];
177
+ const files = fs.readdirSync(TOOLS_DIR).filter(f => f.endsWith('.js'));
178
+
179
+ let isGroq = false;
180
+ try {
181
+ const { getConfig } = require('./config');
182
+ const cfg = getConfig();
183
+ if (cfg.providerUrl && cfg.providerUrl.toLowerCase().includes('groq.com')) {
184
+ isGroq = true;
185
+ }
186
+ } catch (e) {}
187
+
188
+ let disabledTools = new Set();
189
+ try {
190
+ const { getConfig } = require('./config');
191
+ const cfg = getConfig();
192
+ if (Array.isArray(cfg.disabledTools)) disabledTools = new Set(cfg.disabledTools);
193
+ } catch (e) {}
194
+
195
+ const GROQ_ALLOWED = new Set([
196
+ 'read_file', 'write_file', 'list_dir', 'bash', 'shell_command',
197
+ 'soul', 'memory', 'memory_write', 'memory_search', 'filesystem', 'grep_search'
198
+ ]);
199
+
200
+ for (const file of files) {
201
+ try {
202
+ const toolPath = path.join(TOOLS_DIR, file);
203
+ const mod = require(toolPath);
204
+ const toolName = mod.name || path.basename(file, '.js');
205
+
206
+ if (isGroq && !GROQ_ALLOWED.has(toolName)) continue;
207
+ if (disabledTools.has(toolName)) continue;
208
+
209
+ const meta = {
210
+ name: toolName,
211
+ description: mod.description || `${path.basename(file, '.js')} tool`,
212
+ parameters: mod.parameters || mod.inputSchema || { type: 'object', properties: {} },
213
+ execute: mod.execute || (mod.default && mod.default.execute) || null,
214
+ emoji: EMOJI_MAP[toolName] || '',
215
+ toolset: TOOLSET_MAP[toolName] || 'general',
216
+ checkFn: CHECK_FN_MAP[toolName] || null,
217
+ };
218
+
219
+ if (meta.execute) {
220
+ tools.push(meta);
221
+ // Registry'ye kaydet
222
+ globalRegistry.register({
223
+ name: meta.name,
224
+ toolset: meta.toolset,
225
+ schema: { name: meta.name, description: meta.description, parameters: meta.parameters },
226
+ handler: meta.execute,
227
+ checkFn: meta.checkFn,
228
+ emoji: meta.emoji,
229
+ });
230
+ }
231
+ } catch (e) {
232
+ // Sessizce atla
233
+ }
234
+ }
235
+ return tools;
236
+ }
237
+
238
+ const ALIAS_MAP = {
239
+ 'brave_search': 'duckduckgo_search', 'brave-web-search': 'duckduckgo_search',
240
+ 'google_search': 'duckduckgo_search', 'web_search': 'duckduckgo_search',
241
+ 'browse': 'browser', 'shell': 'bash', 'bash_command': 'bash',
242
+ 'execute_command': 'bash', 'run_command': 'bash',
243
+ 'http': 'http_request',
244
+ };
245
+
246
+ const BLOCKED_NAMES = new Set([
247
+ 'brave_search', 'brave-web-search', 'google_search', 'web_search',
248
+ 'browse', 'open', 'search', 'shell', 'bash_command', 'execute_command',
249
+ 'run_command', 'sql', 'query', 'lookup', 'http',
250
+ ]);
251
+
252
+ // ── check_fn TTL cache (Hermes-style, ~30s) ────────────────────────────
253
+ const _checkFnCache = new Map();
254
+ function _cachedCheckFn(fn, key) {
255
+ const now = Date.now();
256
+ const cached = _checkFnCache.get(key);
257
+ if (cached && now - cached.ts < 30000) return cached.result;
258
+ let result;
259
+ try { result = fn() !== false; } catch { result = false; }
260
+ _checkFnCache.set(key, { result, ts: now });
261
+ return result;
262
+ }
263
+
264
+ function toOpenAIFormat(toolDefs) {
265
+ return toolDefs
266
+ .filter(t => !BLOCKED_NAMES.has(t.name))
267
+ .filter(t => {
268
+ if (t.checkFn) {
269
+ return _cachedCheckFn(t.checkFn, t.name);
270
+ }
271
+ return true;
272
+ })
273
+ .map(t => {
274
+ let name = t.name;
275
+ if (ALIAS_MAP[name]) name = ALIAS_MAP[name];
276
+
277
+ const cleanParams = JSON.parse(JSON.stringify(t.parameters || {}));
278
+ if (cleanParams.properties) {
279
+ Object.keys(cleanParams.properties).forEach(key => {
280
+ const prop = cleanParams.properties[key];
281
+ if (Array.isArray(prop.type)) prop.type = prop.type[0];
282
+ delete prop.additionalProperties;
283
+ });
284
+ }
285
+
286
+ return {
287
+ type: 'function',
288
+ function: { name, description: t.description, parameters: cleanParams },
289
+ };
290
+ });
291
+ }
292
+
293
+ async function executeTool(toolName, args, toolDefs) {
294
+ const tool = toolDefs.find(t => t.name === toolName);
295
+ if (!tool) return { error: `Tool bulunamadı: ${toolName}` };
296
+ if (!tool.execute) return { error: `Tool execute fonksiyonu yok: ${toolName}` };
297
+ // checkFn — tool disabled? (re-check at runtime with cache)
298
+ if (tool.checkFn) {
299
+ if (!_cachedCheckFn(tool.checkFn, tool.name)) {
300
+ return { error: `${toolName} şu anda kullanılamıyor (check_fn engelledi)` };
301
+ }
302
+ }
303
+
304
+ // Sandbox restrictions for bash/shell_command
305
+ if (toolName === 'bash' || toolName === 'shell_command') {
306
+ const level = _getSandboxLevel();
307
+ const cmd = (args && args.command) || '';
308
+ if (level === 'strict' && sandbox.isNetworkCommand(cmd)) {
309
+ return { error: 'strict sandbox: network komutları engellendi. Daha düşük sandbox seviyesi kullanın.' };
310
+ }
311
+ }
312
+
313
+ try {
314
+ const result = await tool.execute(args || {});
315
+ return { result };
316
+ } catch (e) {
317
+ return { error: e.message || String(e) };
318
+ }
319
+ }
320
+
321
+ module.exports = {
322
+ loadToolDefinitions, toOpenAIFormat, executeTool, getToolsForProvider,
323
+ EMOJI_MAP, TOOLSET_MAP, CHECK_FN_MAP,
324
+ };