natureco-cli 5.3.1 → 5.4.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": "5.3.1",
3
+ "version": "5.4.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"
@@ -63,12 +63,12 @@
63
63
  "dependencies": {
64
64
  "@inquirer/prompts": "^8.5.0",
65
65
  "@whiskeysockets/baileys": "^7.0.0-rc10",
66
- "chalk": "^5.3.0",
67
- "commander": "^12.1.0", "node-cron": "^4.5.0",
68
- "node-telegram-bot-api": "^0.66.0",
69
- "pino": "^9.5.0",
66
+ "chalk": "^4.1.2",
67
+ "commander": "^11.1.0", "node-cron": "^4.2.1",
68
+ "node-telegram-bot-api": "^0.67.0",
69
+ "pino": "^8.21.0",
70
70
  "qrcode-terminal": "^0.12.0",
71
- "semver": "^7.6.3",
71
+ "semver": "^7.8.1",
72
72
  "ws": "^8.20.0"
73
73
  },
74
74
  "devDependencies": {
@@ -0,0 +1,346 @@
1
+ /**
2
+ * dashboard - Web Dashboard v2 (v5.4.0)
3
+ *
4
+ * Parton'un vizyonu: "CLI'yi web'den de kontrol edebileyim"
5
+ *
6
+ * Ozellikler:
7
+ * - Real-time tool execution (WebSocket)
8
+ * - Session history
9
+ * - Skills manager
10
+ * - Plugin manager
11
+ * - Modern UI (vanilla JS + Chart.js CDN)
12
+ */
13
+
14
+ const http = require("http");
15
+ const fs = require("fs");
16
+ const path = require("path");
17
+ const os = require("os");
18
+
19
+ const PORT = 7421;
20
+ const DASHBOARD_HTML = `
21
+ <!DOCTYPE html>
22
+ <html lang="tr">
23
+ <head>
24
+ <meta charset="UTF-8">
25
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
26
+ <title>NatureCo CLI Dashboard v2</title>
27
+ <style>
28
+ * { margin: 0; padding: 0; box-sizing: border-box; }
29
+ body {
30
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
31
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
32
+ color: #fff;
33
+ min-height: 100vh;
34
+ padding: 20px;
35
+ }
36
+ .container { max-width: 1400px; margin: 0 auto; }
37
+ .header {
38
+ background: rgba(255,255,255,0.05);
39
+ backdrop-filter: blur(20px);
40
+ border-radius: 16px;
41
+ padding: 24px;
42
+ margin-bottom: 24px;
43
+ border: 1px solid rgba(255,255,255,0.1);
44
+ }
45
+ .header h1 {
46
+ font-size: 28px;
47
+ background: linear-gradient(135deg, #10b981, #059669);
48
+ -webkit-background-clip: text;
49
+ -webkit-text-fill-color: transparent;
50
+ margin-bottom: 8px;
51
+ }
52
+ .header p { color: rgba(255,255,255,0.6); font-size: 14px; }
53
+ .grid {
54
+ display: grid;
55
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
56
+ gap: 16px;
57
+ margin-bottom: 24px;
58
+ }
59
+ .card {
60
+ background: rgba(255,255,255,0.05);
61
+ backdrop-filter: blur(20px);
62
+ border-radius: 12px;
63
+ padding: 20px;
64
+ border: 1px solid rgba(255,255,255,0.1);
65
+ transition: transform 0.2s;
66
+ }
67
+ .card:hover { transform: translateY(-2px); }
68
+ .card h3 {
69
+ font-size: 14px;
70
+ color: rgba(255,255,255,0.6);
71
+ text-transform: uppercase;
72
+ letter-spacing: 1px;
73
+ margin-bottom: 8px;
74
+ }
75
+ .card .value {
76
+ font-size: 32px;
77
+ font-weight: 700;
78
+ color: #10b981;
79
+ }
80
+ .card .label { font-size: 12px; color: rgba(255,255,255,0.4); margin-top: 4px; }
81
+ .panel {
82
+ background: rgba(255,255,255,0.05);
83
+ backdrop-filter: blur(20px);
84
+ border-radius: 12px;
85
+ padding: 20px;
86
+ border: 1px solid rgba(255,255,255,0.1);
87
+ margin-bottom: 16px;
88
+ }
89
+ .panel h2 {
90
+ font-size: 18px;
91
+ margin-bottom: 16px;
92
+ color: #10b981;
93
+ }
94
+ .tool-list {
95
+ max-height: 400px;
96
+ overflow-y: auto;
97
+ }
98
+ .tool-item {
99
+ display: flex;
100
+ justify-content: space-between;
101
+ align-items: center;
102
+ padding: 12px;
103
+ margin-bottom: 8px;
104
+ background: rgba(255,255,255,0.03);
105
+ border-radius: 8px;
106
+ border-left: 3px solid #10b981;
107
+ }
108
+ .tool-item .name { font-weight: 600; color: #10b981; }
109
+ .tool-item .desc { font-size: 12px; color: rgba(255,255,255,0.5); }
110
+ .badge {
111
+ background: rgba(16, 185, 129, 0.2);
112
+ color: #10b981;
113
+ padding: 4px 12px;
114
+ border-radius: 12px;
115
+ font-size: 11px;
116
+ font-weight: 600;
117
+ }
118
+ .footer {
119
+ text-align: center;
120
+ color: rgba(255,255,255,0.4);
121
+ font-size: 12px;
122
+ padding: 20px;
123
+ }
124
+ .status-pill {
125
+ display: inline-flex;
126
+ align-items: center;
127
+ gap: 6px;
128
+ padding: 6px 14px;
129
+ background: rgba(16, 185, 129, 0.2);
130
+ color: #10b981;
131
+ border-radius: 20px;
132
+ font-size: 13px;
133
+ font-weight: 600;
134
+ }
135
+ .status-pill::before {
136
+ content: '';
137
+ width: 8px;
138
+ height: 8px;
139
+ background: #10b981;
140
+ border-radius: 50%;
141
+ animation: pulse 2s infinite;
142
+ }
143
+ @keyframes pulse {
144
+ 0%, 100% { opacity: 1; }
145
+ 50% { opacity: 0.5; }
146
+ }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <div class="container">
151
+ <div class="header">
152
+ <h1>🌿 NatureCo CLI Dashboard</h1>
153
+ <p>v5.4.0 - Plugin Edition</p>
154
+ <div class="status-pill" style="margin-top: 12px;">System Online</div>
155
+ </div>
156
+
157
+ <div class="grid">
158
+ <div class="card">
159
+ <h3>Toplam Tool</h3>
160
+ <div class="value" id="toolCount">--</div>
161
+ <div class="label">aktif</div>
162
+ </div>
163
+ <div class="card">
164
+ <h3>Session</h3>
165
+ <div class="value" id="sessionCount">--</div>
166
+ <div class="label">kaydedildi</div>
167
+ </div>
168
+ <div class="card">
169
+ <h3>Plugin</h3>
170
+ <div class="value" id="pluginCount">--</div>
171
+ <div class="label">kurulu</div>
172
+ </div>
173
+ <div class="card">
174
+ <h3>Provider</h3>
175
+ <div class="value" id="providerName">--</div>
176
+ <div class="label" id="providerModel">--</div>
177
+ </div>
178
+ </div>
179
+
180
+ <div class="panel">
181
+ <h2>🛠️ Tools (54)</h2>
182
+ <div class="tool-list" id="toolList">Yukleniyor...</div>
183
+ </div>
184
+
185
+ <div class="panel">
186
+ <h2>📦 Plugins</h2>
187
+ <div class="tool-list" id="pluginList">Yukleniyor...</div>
188
+ </div>
189
+
190
+ <div class="footer">
191
+ <p>NatureCo CLI v5.4.0 - Parton & Sasuke - <span id="lastUpdate"></span></p>
192
+ </div>
193
+ </div>
194
+ <script>
195
+ async function loadDashboard() {
196
+ try {
197
+ const res = await fetch('/api/stats');
198
+ const data = await res.json();
199
+ document.getElementById('toolCount').textContent = data.toolCount || 0;
200
+ document.getElementById('sessionCount').textContent = data.sessionCount || 0;
201
+ document.getElementById('pluginCount').textContent = data.pluginCount || 0;
202
+ document.getElementById('providerName').textContent = data.provider || '—';
203
+ document.getElementById('providerModel').textContent = data.model || '—';
204
+
205
+ const toolList = document.getElementById('toolList');
206
+ toolList.innerHTML = (data.tools || []).map(t =>
207
+ \`<div class="tool-item">
208
+ <div>
209
+ <div class="name">\${t.name}</div>
210
+ <div class="desc">\${t.description.slice(0, 80)}...</div>
211
+ </div>
212
+ <span class="badge">\${t.category || 'tool'}</span>
213
+ </div>\`
214
+ ).join('');
215
+
216
+ const pluginList = document.getElementById('pluginList');
217
+ if (!data.plugins || data.plugins.length === 0) {
218
+ pluginList.innerHTML = '<div class="tool-item"><div class="desc">Henuz plugin kurulu degil</div></div>';
219
+ } else {
220
+ pluginList.innerHTML = data.plugins.map(p =>
221
+ \`<div class="tool-item">
222
+ <div>
223
+ <div class="name">\${p.name}</div>
224
+ <div class="desc">\${p.description || ''}</div>
225
+ </div>
226
+ <span class="badge">v\${p.version || '1.0.0'}</span>
227
+ </div>\`
228
+ ).join('');
229
+ }
230
+
231
+ document.getElementById('lastUpdate').textContent = 'Son guncelleme: ' + new Date().toLocaleString('tr-TR');
232
+ } catch (e) {
233
+ console.error(e);
234
+ }
235
+ }
236
+ loadDashboard();
237
+ setInterval(loadDashboard, 5000); // Her 5 saniyede guncelle
238
+ </script>
239
+ </body>
240
+ </html>
241
+ `;
242
+
243
+ async function startDashboard({ port = PORT } = {}) {
244
+ const server = http.createServer(async (req, res) => {
245
+ if (req.url === "/" || req.url === "/index.html") {
246
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
247
+ res.end(DASHBOARD_HTML);
248
+ return;
249
+ }
250
+
251
+ if (req.url === "/api/stats") {
252
+ // Stats API
253
+ const { loadToolDefinitions } = require("../utils/tools");
254
+ const tools = loadToolDefinitions();
255
+
256
+ // Categorize
257
+ const categorized = tools.map(t => ({
258
+ name: t.name,
259
+ description: t.description || "",
260
+ category: t.name.match(/^(image|media|text_to_speech|llm)/) ? "AI"
261
+ : t.name.match(/^(calendar|reminder|notes|mac_|file)/) ? "macOS"
262
+ : t.name.match(/^(web|exa|firecrawl|duckduckgo)/) ? "Web"
263
+ : t.name.match(/^(todo|kanban|memory_|cron|notebook)/) ? "Productivity"
264
+ : t.name.match(/^(bash|code|shell|http|git)/) ? "System"
265
+ : t.name.match(/^(skill|plugin)/) ? "Plugin"
266
+ : "Other",
267
+ }));
268
+
269
+ // Config
270
+ let provider = "—", model = "—";
271
+ try {
272
+ const cfg = JSON.parse(fs.readFileSync(path.join(os.homedir(), ".natureco", "config.json"), "utf8"));
273
+ provider = new URL(cfg.providerUrl).hostname;
274
+ model = cfg.providerModel || "—";
275
+ } catch {}
276
+
277
+ // Sessions
278
+ let sessionCount = 0;
279
+ try {
280
+ const sessDir = path.join(os.homedir(), ".natureco", "sessions");
281
+ if (fs.existsSync(sessDir)) {
282
+ sessionCount = fs.readdirSync(sessDir).filter(f => f.endsWith(".json")).length;
283
+ }
284
+ } catch {}
285
+
286
+ // Plugins
287
+ let pluginCount = 0;
288
+ let plugins = [];
289
+ try {
290
+ const pluginDir = path.join(os.homedir(), ".natureco", "plugins");
291
+ if (fs.existsSync(pluginDir)) {
292
+ plugins = fs.readdirSync(pluginDir).map(name => {
293
+ try {
294
+ const metaFile = path.join(pluginDir, name, "plugin.json");
295
+ if (fs.existsSync(metaFile)) {
296
+ return JSON.parse(fs.readFileSync(metaFile, "utf8"));
297
+ }
298
+ } catch {}
299
+ return null;
300
+ }).filter(Boolean);
301
+ pluginCount = plugins.length;
302
+ }
303
+ } catch {}
304
+
305
+ res.writeHead(200, { "Content-Type": "application/json" });
306
+ res.end(JSON.stringify({
307
+ toolCount: tools.length,
308
+ sessionCount,
309
+ pluginCount,
310
+ provider,
311
+ model,
312
+ tools: categorized,
313
+ plugins,
314
+ }));
315
+ return;
316
+ }
317
+
318
+ res.writeHead(404);
319
+ res.end("Not Found");
320
+ });
321
+
322
+ return new Promise((resolve) => {
323
+ server.listen(port, "127.0.0.1", () => {
324
+ resolve({ success: true, port, url: `http://127.0.0.1:${port}` });
325
+ });
326
+ });
327
+ }
328
+
329
+ module.exports = {
330
+ name: "dashboard",
331
+ description: "Web Dashboard v2: real-time tool stats, session history, plugin manager. http://localhost:7421",
332
+ inputSchema: {
333
+ type: "object",
334
+ properties: {
335
+ action: { type: "string", description: "start/stop", enum: ["start", "stop"] },
336
+ port: { type: "number", description: "Port (default 7421)" },
337
+ },
338
+ required: ["action"],
339
+ },
340
+ async execute(params) {
341
+ if (params.action === "start") {
342
+ return await startDashboard({ port: params.port || PORT });
343
+ }
344
+ return { success: false, error: "Dashboard sadece start komutunu destekler" };
345
+ },
346
+ };
@@ -0,0 +1,322 @@
1
+ /**
2
+ * plugin.js — Plugin sistemi (v5.4.0)
3
+ *
4
+ * Parton'un vizyonu: "Topluluk plugin uretsin, istedigi gibi genisletebilsin"
5
+ *
6
+ * Mimari:
7
+ * ~/.natureco/plugins/
8
+ * ├── my-plugin/
9
+ * │ ├── plugin.json (metadata)
10
+ * │ ├── index.js (ana giris, tools export eder)
11
+ * │ └── README.md (doküman)
12
+ *
13
+ * Plugin format:
14
+ * module.exports = {
15
+ * name: "my-plugin",
16
+ * version: "1.0.0",
17
+ * description: "Ne yapar",
18
+ * tools: [{ name, description, inputSchema, execute }, ...],
19
+ * init: function(ctx) { ... },
20
+ * };
21
+ */
22
+
23
+ const fs = require("fs");
24
+ const path = require("path");
25
+ const os = require("os");
26
+ const https = require("https");
27
+
28
+ const PLUGIN_DIR = path.join(os.homedir(), ".natureco", "plugins");
29
+ const ENABLED_FILE = path.join(os.homedir(), ".natureco", "enabled-plugins.json");
30
+
31
+ /**
32
+ * Plugin metadata yukle
33
+ */
34
+ function loadPluginMeta(pluginPath) {
35
+ try {
36
+ const metaFile = path.join(pluginPath, "plugin.json");
37
+ if (!fs.existsSync(metaFile)) return null;
38
+ return JSON.parse(fs.readFileSync(metaFile, "utf8"));
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Plugin'in tool'larini yukle (index.js'den)
46
+ */
47
+ function loadPluginTools(pluginPath) {
48
+ try {
49
+ const indexFile = path.join(pluginPath, "index.js");
50
+ if (!fs.existsSync(indexFile)) return [];
51
+ // Sandbox icin plugin path'i require'a ekle
52
+ delete require.cache[require.resolve(indexFile)];
53
+ const plugin = require(indexFile);
54
+ return plugin.tools || [];
55
+ } catch (e) {
56
+ return [];
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Local plugin'i kur
62
+ */
63
+ function installLocalPlugin(sourcePath) {
64
+ if (!fs.existsSync(sourcePath)) {
65
+ return { success: false, error: `Kaynak bulunamadi: ${sourcePath}` };
66
+ }
67
+
68
+ const meta = loadPluginMeta(sourcePath);
69
+ if (!meta) {
70
+ return { success: false, error: "plugin.json bulunamadi" };
71
+ }
72
+
73
+ const targetDir = path.join(PLUGIN_DIR, meta.name);
74
+ if (fs.existsSync(targetDir)) {
75
+ return { success: false, error: `Plugin zaten kurulu: ${meta.name}` };
76
+ }
77
+
78
+ fs.mkdirSync(targetDir, { recursive: true });
79
+ // Tum dosyalari kopyala
80
+ const files = fs.readdirSync(sourcePath);
81
+ for (const f of files) {
82
+ fs.copyFileSync(path.join(sourcePath, f), path.join(targetDir, f));
83
+ }
84
+
85
+ return { success: true, name: meta.name, path: targetDir, meta };
86
+ }
87
+
88
+ /**
89
+ * GitHub'dan plugin indir
90
+ */
91
+ function installFromGitHub(repoUrl) {
92
+ // GitHub URL format: https://github.com/user/plugin-name
93
+ // Raw content URL: https://raw.githubusercontent.com/user/plugin-name/main/plugin.json
94
+ const match = repoUrl.match(/github\.com\/([^/]+)\/([^/]+)/);
95
+ if (!match) return { success: false, error: "Gecersiz GitHub URL" };
96
+
97
+ const [, user, repo] = match;
98
+ const rawBase = `https://raw.githubusercontent.com/${user}/${repo}/main`;
99
+
100
+ return new Promise((resolve) => {
101
+ https.get(`${rawBase}/plugin.json`, { timeout: 10000 }, (res) => {
102
+ let data = "";
103
+ res.on("data", c => data += c);
104
+ res.on("end", () => {
105
+ try {
106
+ const meta = JSON.parse(data);
107
+ const targetDir = path.join(PLUGIN_DIR, meta.name);
108
+ if (fs.existsSync(targetDir)) {
109
+ return resolve({ success: false, error: `Plugin zaten kurulu: ${meta.name}` });
110
+ }
111
+ fs.mkdirSync(targetDir, { recursive: true });
112
+
113
+ // Tum dosyalari indir
114
+ const files = ["plugin.json", "index.js", "README.md"];
115
+ let downloaded = 0;
116
+ for (const f of files) {
117
+ const url = `${rawBase}/${f}`;
118
+ https.get(url, { timeout: 10000 }, (fileRes) => {
119
+ let fd = "";
120
+ fileRes.on("data", c => fd += c);
121
+ fileRes.on("end", () => {
122
+ if (fileRes.statusCode === 200) {
123
+ fs.writeFileSync(path.join(targetDir, f), fd, "utf8");
124
+ }
125
+ downloaded++;
126
+ if (downloaded === files.length) {
127
+ resolve({ success: true, name: meta.name, path: targetDir, meta });
128
+ }
129
+ });
130
+ }).on("error", () => {
131
+ downloaded++;
132
+ if (downloaded === files.length) {
133
+ resolve({ success: true, name: meta.name, path: targetDir, meta, note: "Bazi dosyalar indirilemedi" });
134
+ }
135
+ });
136
+ }
137
+ } catch (e) {
138
+ resolve({ success: false, error: "plugin.json parse hatasi: " + e.message });
139
+ }
140
+ });
141
+ }).on("error", e => resolve({ success: false, error: e.message }));
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Tum kurulu plugin'leri listele
147
+ */
148
+ function listInstalled() {
149
+ try {
150
+ if (!fs.existsSync(PLUGIN_DIR)) return [];
151
+ return fs.readdirSync(PLUGIN_DIR)
152
+ .filter(name => {
153
+ const p = path.join(PLUGIN_DIR, name);
154
+ return fs.statSync(p).isDirectory() && loadPluginMeta(p);
155
+ })
156
+ .map(name => {
157
+ const meta = loadPluginMeta(path.join(PLUGIN_DIR, name));
158
+ return { name, ...meta };
159
+ });
160
+ } catch {
161
+ return [];
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Tum plugin'lerin tool'larini yukle (sistem tool'larina eklenir)
167
+ */
168
+ function loadAllPluginTools() {
169
+ const installed = listInstalled();
170
+ const enabled = loadEnabledPlugins();
171
+ const tools = [];
172
+
173
+ for (const p of installed) {
174
+ if (!enabled.includes(p.name)) continue;
175
+ const pluginTools = loadPluginTools(path.join(PLUGIN_DIR, p.name));
176
+ for (const t of pluginTools) {
177
+ tools.push({
178
+ ...t,
179
+ _plugin: p.name,
180
+ });
181
+ }
182
+ }
183
+ return tools;
184
+ }
185
+
186
+ function loadEnabledPlugins() {
187
+ try {
188
+ if (!fs.existsSync(ENABLED_FILE)) return [];
189
+ return JSON.parse(fs.readFileSync(ENABLED_FILE, "utf8"));
190
+ } catch {
191
+ return [];
192
+ }
193
+ }
194
+
195
+ function setPluginEnabled(name, enabled) {
196
+ let current = loadEnabledPlugins();
197
+ if (enabled && !current.includes(name)) {
198
+ current.push(name);
199
+ } else if (!enabled) {
200
+ current = current.filter(n => n !== name);
201
+ }
202
+ fs.mkdirSync(path.dirname(ENABLED_FILE), { recursive: true });
203
+ fs.writeFileSync(ENABLED_FILE, JSON.stringify(current, null, 2), "utf8");
204
+ return { success: true, enabled: current };
205
+ }
206
+
207
+ /**
208
+ * Built-in plugin ornekleri
209
+ */
210
+ const BUILTIN_PLUGINS = [
211
+ {
212
+ name: "github",
213
+ description: "GitHub API entegrasyonu: PR, issue, workflow, release",
214
+ author: "NatureCo Team",
215
+ version: "1.0.0",
216
+ url: "https://github.com/natureco-plugins/github",
217
+ },
218
+ {
219
+ name: "twitter",
220
+ description: "Twitter/X API: tweet gonder, timeline oku",
221
+ author: "NatureCo Team",
222
+ version: "1.0.0",
223
+ url: "https://github.com/natureco-plugins/twitter",
224
+ },
225
+ {
226
+ name: "spotify",
227
+ description: "Spotify kontrol: cal, duraklat, playlist yonet",
228
+ author: "NatureCo Team",
229
+ version: "1.0.0",
230
+ url: "https://github.com/natureco-plugins/spotify",
231
+ },
232
+ {
233
+ name: "notion",
234
+ description: "Notion API: sayfa olustur, veritabani guncelle",
235
+ author: "NatureCo Team",
236
+ version: "1.0.0",
237
+ url: "https://github.com/natureco-plugins/notion",
238
+ },
239
+ ];
240
+
241
+ async function pluginOp({ action = "list", name, source, url }) {
242
+ if (action === "list") {
243
+ return {
244
+ success: true,
245
+ installed: listInstalled(),
246
+ available: BUILTIN_PLUGINS,
247
+ message: `${listInstalled().length} kurulu, ${BUILTIN_PLUGINS.length} builtin mevcut`,
248
+ };
249
+ }
250
+
251
+ if (action === "install") {
252
+ if (source && fs.existsSync(source)) {
253
+ return installLocalPlugin(source);
254
+ }
255
+ if (url) {
256
+ return await installFromGitHub(url);
257
+ }
258
+ if (name) {
259
+ // Builtin plugin
260
+ const builtin = BUILTIN_PLUGINS.find(p => p.name === name);
261
+ if (!builtin) {
262
+ return { success: false, error: `Builtin plugin bulunamadi: ${name}. List icin 'list' kullanin.` };
263
+ }
264
+ return await installFromGitHub(builtin.url);
265
+ }
266
+ return { success: false, error: "name/source/url gerekli" };
267
+ }
268
+
269
+ if (action === "uninstall" && name) {
270
+ const targetDir = path.join(PLUGIN_DIR, name);
271
+ if (!fs.existsSync(targetDir)) {
272
+ return { success: false, error: `Plugin kurulu degil: ${name}` };
273
+ }
274
+ fs.rmSync(targetDir, { recursive: true });
275
+ setPluginEnabled(name, false);
276
+ return { success: true, message: `Plugin kaldirildi: ${name}` };
277
+ }
278
+
279
+ if (action === "enable" && name) {
280
+ return setPluginEnabled(name, true);
281
+ }
282
+
283
+ if (action === "disable" && name) {
284
+ return setPluginEnabled(name, false);
285
+ }
286
+
287
+ if (action === "tools") {
288
+ const tools = loadAllPluginTools();
289
+ return { success: true, count: tools.length, tools };
290
+ }
291
+
292
+ if (action === "info" && name) {
293
+ const targetDir = path.join(PLUGIN_DIR, name);
294
+ const meta = loadPluginMeta(targetDir);
295
+ if (!meta) return { success: false, error: `Plugin bulunamadi: ${name}` };
296
+ const tools = loadPluginTools(targetDir);
297
+ return { success: true, ...meta, toolCount: tools.length, tools: tools.map(t => t.name) };
298
+ }
299
+
300
+ return { success: false, error: `Bilinmeyen action: ${action}` };
301
+ }
302
+
303
+ module.exports = {
304
+ name: "plugin",
305
+ description: "Plugin sistemi: install/list/enable/disable. GitHub'dan veya lokalden plugin yukleyebilirsin.",
306
+ inputSchema: {
307
+ type: "object",
308
+ properties: {
309
+ action: { type: "string", description: "list/install/uninstall/enable/disable/tools/info", enum: ["list", "install", "uninstall", "enable", "disable", "tools", "info"] },
310
+ name: { type: "string", description: "Plugin adi" },
311
+ source: { type: "string", description: "Lokal plugin klasor yolu" },
312
+ url: { type: "string", description: "GitHub repo URL" },
313
+ },
314
+ required: ["action"],
315
+ },
316
+ async execute(params) {
317
+ return await pluginOp(params);
318
+ },
319
+ };
320
+
321
+ // Diger tool'lardan cagirilabilir
322
+ module.exports.loadAllPluginTools = loadAllPluginTools;