natureco-cli 2.23.31 → 4.4.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.
Files changed (174) hide show
  1. package/AUDIT.md +178 -0
  2. package/CHANGELOG.md +422 -0
  3. package/DEPLOY_v2.0.0.md +400 -0
  4. package/README.md +159 -1
  5. package/bin/natureco.js +182 -12
  6. package/package.json +43 -11
  7. package/skills/code-review/SKILL.md +0 -0
  8. package/skills/summarize/SKILL.md +0 -0
  9. package/skills/translate/SKILL.md +0 -0
  10. package/src/commands/acp.js +0 -0
  11. package/src/commands/admin-rpc.js +0 -0
  12. package/src/commands/agent.js +0 -0
  13. package/src/commands/agents.js +0 -0
  14. package/src/commands/approvals.js +0 -0
  15. package/src/commands/ask.js +1 -1
  16. package/src/commands/audit.js +209 -0
  17. package/src/commands/backup.js +0 -0
  18. package/src/commands/bonjour.js +0 -0
  19. package/src/commands/bots.js +0 -0
  20. package/src/commands/browser.js +0 -0
  21. package/src/commands/capability.js +0 -0
  22. package/src/commands/channels.js +0 -0
  23. package/src/commands/chat.js +0 -0
  24. package/src/commands/clawbot.js +0 -0
  25. package/src/commands/clickclack.js +0 -0
  26. package/src/commands/code.js +0 -0
  27. package/src/commands/commands.js +0 -0
  28. package/src/commands/commitments.js +0 -0
  29. package/src/commands/completion.js +0 -0
  30. package/src/commands/config.js +11 -2
  31. package/src/commands/configure.js +0 -0
  32. package/src/commands/cost.js +210 -0
  33. package/src/commands/crestodian.js +0 -0
  34. package/src/commands/cron.js +0 -0
  35. package/src/commands/daemon.js +0 -0
  36. package/src/commands/dashboard.js +126 -45
  37. package/src/commands/device-pair.js +0 -0
  38. package/src/commands/devices.js +0 -0
  39. package/src/commands/directory.js +0 -0
  40. package/src/commands/discord.js +0 -0
  41. package/src/commands/dns.js +0 -0
  42. package/src/commands/docs.js +0 -0
  43. package/src/commands/doctor.js +134 -15
  44. package/src/commands/exec-policy.js +0 -0
  45. package/src/commands/gateway-server.js +0 -0
  46. package/src/commands/gateway.js +0 -0
  47. package/src/commands/git.js +0 -0
  48. package/src/commands/health.js +0 -0
  49. package/src/commands/help.js +0 -0
  50. package/src/commands/hooks.js +0 -0
  51. package/src/commands/imessage.js +0 -0
  52. package/src/commands/infer.js +0 -0
  53. package/src/commands/init.js +0 -0
  54. package/src/commands/irc.js +0 -0
  55. package/src/commands/login.js +0 -0
  56. package/src/commands/logout.js +0 -0
  57. package/src/commands/logs.js +0 -0
  58. package/src/commands/mattermost.js +0 -0
  59. package/src/commands/mcp.js +0 -0
  60. package/src/commands/medium.js +206 -0
  61. package/src/commands/memory-cmd.js +0 -0
  62. package/src/commands/memory.js +0 -0
  63. package/src/commands/message.js +0 -0
  64. package/src/commands/migrate.js +0 -0
  65. package/src/commands/models.js +0 -0
  66. package/src/commands/naturehub.js +156 -0
  67. package/src/commands/node.js +0 -0
  68. package/src/commands/nodes.js +1 -1
  69. package/src/commands/oc-path.js +0 -0
  70. package/src/commands/onboard.js +0 -0
  71. package/src/commands/open-prose.js +0 -0
  72. package/src/commands/pairing.js +0 -0
  73. package/src/commands/path.js +0 -0
  74. package/src/commands/plugins.js +0 -0
  75. package/src/commands/policy.js +0 -0
  76. package/src/commands/proxy.js +0 -0
  77. package/src/commands/qr.js +0 -0
  78. package/src/commands/reset.js +0 -0
  79. package/src/commands/run.js +0 -0
  80. package/src/commands/sandbox.js +0 -0
  81. package/src/commands/secrets.js +0 -0
  82. package/src/commands/security.js +0 -0
  83. package/src/commands/seo.js +268 -0
  84. package/src/commands/sessions.js +0 -0
  85. package/src/commands/setup.js +131 -4
  86. package/src/commands/signal.js +0 -0
  87. package/src/commands/skills.js +82 -1
  88. package/src/commands/slack.js +0 -0
  89. package/src/commands/sms.js +0 -0
  90. package/src/commands/status.js +0 -0
  91. package/src/commands/system.js +0 -0
  92. package/src/commands/tasks.js +0 -0
  93. package/src/commands/team.js +171 -0
  94. package/src/commands/telegram.js +0 -0
  95. package/src/commands/terminal.js +0 -0
  96. package/src/commands/thread-ownership.js +0 -0
  97. package/src/commands/transcripts.js +0 -0
  98. package/src/commands/tui.js +0 -0
  99. package/src/commands/ultrareview.js +0 -0
  100. package/src/commands/uninstall.js +0 -0
  101. package/src/commands/update.js +0 -0
  102. package/src/commands/voice.js +0 -0
  103. package/src/commands/vydra.js +0 -0
  104. package/src/commands/web-fetch.js +34 -0
  105. package/src/commands/webhooks.js +0 -0
  106. package/src/commands/whatsapp.js +0 -0
  107. package/src/commands/wiki.js +0 -0
  108. package/src/commands/workboard.js +0 -0
  109. package/src/commands/xp.js +194 -0
  110. package/src/tools/audio_understanding.js +0 -0
  111. package/src/tools/bash.js +0 -0
  112. package/src/tools/browser.js +0 -0
  113. package/src/tools/canvas.js +0 -0
  114. package/src/tools/document_extract.js +0 -0
  115. package/src/tools/duckduckgo.js +0 -0
  116. package/src/tools/exa_search.js +0 -0
  117. package/src/tools/filesystem.js +0 -0
  118. package/src/tools/firecrawl.js +0 -0
  119. package/src/tools/git.js +0 -0
  120. package/src/tools/http.js +0 -0
  121. package/src/tools/image_generation.js +0 -0
  122. package/src/tools/list_dir.js +0 -0
  123. package/src/tools/llm_task.js +43 -11
  124. package/src/tools/media_understanding.js +0 -0
  125. package/src/tools/music_generation.js +0 -0
  126. package/src/tools/parallel_search.js +0 -0
  127. package/src/tools/phone_control.js +0 -0
  128. package/src/tools/phone_control_enhanced.js +0 -0
  129. package/src/tools/read_file.js +0 -0
  130. package/src/tools/searxng.js +0 -0
  131. package/src/tools/speech_to_text.js +0 -0
  132. package/src/tools/text_to_speech.js +0 -0
  133. package/src/tools/thread_ownership.js +0 -0
  134. package/src/tools/video_generation.js +0 -0
  135. package/src/tools/web_readability.js +0 -0
  136. package/src/tools/web_search.js +0 -0
  137. package/src/tools/write_file.js +0 -0
  138. package/src/utils/agents-md.js +0 -0
  139. package/src/utils/agents.js +0 -0
  140. package/src/utils/api.js +6 -2
  141. package/src/utils/approvals.js +0 -0
  142. package/src/utils/audit.js +199 -0
  143. package/src/utils/background.js +0 -0
  144. package/src/utils/baileys.js +0 -0
  145. package/src/utils/branding.js +136 -0
  146. package/src/utils/commands.js +0 -0
  147. package/src/utils/config.js +0 -0
  148. package/src/utils/cost-tracker.js +360 -0
  149. package/src/utils/cron.js +0 -0
  150. package/src/utils/dashboard-server.js +284 -0
  151. package/src/utils/errors.js +0 -0
  152. package/src/utils/format.js +7 -10
  153. package/src/utils/gateway-ws.js +0 -0
  154. package/src/utils/headless.js +0 -0
  155. package/src/utils/history.js +0 -0
  156. package/src/utils/hooks.js +0 -0
  157. package/src/utils/inquirer-wrapper.js +0 -0
  158. package/src/utils/mcp-client.js +0 -0
  159. package/src/utils/mcp.js +0 -0
  160. package/src/utils/memory.js +0 -0
  161. package/src/utils/parallel-tools.js +0 -0
  162. package/src/utils/path-utils.js +0 -0
  163. package/src/utils/pattern-detector.js +314 -0
  164. package/src/utils/plugin-registry.js +0 -0
  165. package/src/utils/secret-scanner.js +204 -0
  166. package/src/utils/secrets.js +0 -0
  167. package/src/utils/sessions.js +0 -0
  168. package/src/utils/skills.js +0 -0
  169. package/src/utils/sub-agent.js +6 -0
  170. package/src/utils/token-budget.js +0 -0
  171. package/src/utils/tool-adapter.js +0 -0
  172. package/src/utils/tool-runner.js +0 -0
  173. package/src/utils/tui.js +750 -0
  174. package/src/utils/web-fetch.js +0 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * NatureCo CLI — Audit Log (Phase 2)
3
+ *
4
+ * Tüm kullanıcı işlemlerini (komut, onay, tool call, hata) JSONL olarak kaydeder.
5
+ * Bu log güvenlik denetimi, hata ayıklama ve compliance için kullanılır.
6
+ *
7
+ * Format: Her satır bir JSON objesi. Sıralı append, async-non-blocking.
8
+ *
9
+ * Varsayılan: ~/.natureco/audit/audit-YYYY-MM-DD.jsonl
10
+ * 30 günden eski loglar otomatik temizlenir.
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const os = require('os');
16
+
17
+ const AUDIT_DIR = path.join(os.homedir(), '.natureco', 'audit');
18
+ const RETENTION_DAYS = 30;
19
+
20
+ // Action tipleri
21
+ const ACTIONS = {
22
+ COMMAND_RUN: 'command.run', // Bir CLI komutu çalıştırıldı
23
+ COMMAND_FAIL: 'command.fail', // Komut hata verdi
24
+ APPROVAL_ASK: 'approval.ask', // Onay istendi
25
+ APPROVAL_GRANT: 'approval.grant', // Kullanıcı onayladı
26
+ APPROVAL_DENY: 'approval.deny', // Kullanıcı reddetti
27
+ TOOL_CALL: 'tool.call', // Agent bir tool çağırdı
28
+ TOOL_BLOCK: 'tool.block', // Tehlikeli tool çağrısı engellendi
29
+ AUTH_LOGIN: 'auth.login', // Login
30
+ AUTH_LOGOUT: 'auth.logout', // Logout
31
+ SECRET_READ: 'secret.read', // Secret çözüldü
32
+ SECRET_LEAK: 'secret.leak', // Potansiyel secret sızıntısı tespit
33
+ CONFIG_CHANGE: 'config.change', // Config değişti
34
+ CRON_RUN: 'cron.run', // Cron job çalıştı
35
+ SKILL_INSTALL: 'skill.install', // Skill yüklendi
36
+ SKILL_AUTO: 'skill.auto', // Self-evolving skill oluştu
37
+ ERROR: 'error', // Genel hata
38
+ INFO: 'info', // Bilgi mesajı
39
+ };
40
+
41
+ let _stream = null;
42
+ let _streamPath = null;
43
+ let _writeQueue = Promise.resolve();
44
+
45
+ function ensureDir() {
46
+ if (!fs.existsSync(AUDIT_DIR)) fs.mkdirSync(AUDIT_DIR, { recursive: true });
47
+ }
48
+
49
+ function todayFile() {
50
+ const d = new Date().toISOString().slice(0, 10);
51
+ return path.join(AUDIT_DIR, `audit-${d}.jsonl`);
52
+ }
53
+
54
+ function getStream() {
55
+ const target = todayFile();
56
+ if (_stream && target === _streamPath) return _stream;
57
+ if (_stream) {
58
+ try { _stream.end(); } catch {}
59
+ }
60
+ ensureDir();
61
+ _stream = fs.createWriteStream(target, { flags: 'a' });
62
+ _streamPath = target;
63
+ return _stream;
64
+ }
65
+
66
+ /**
67
+ * Ana audit kayıt fonksiyonu. Non-blocking — queue'ya ekler ve döner.
68
+ *
69
+ * @param {string} action - ACTIONS enum'undan
70
+ * @param {object} data - Ek bağlam
71
+ * @returns {Promise<void>}
72
+ */
73
+ function log(action, data = {}) {
74
+ ensureDir();
75
+ const entry = {
76
+ ts: new Date().toISOString(),
77
+ pid: process.pid,
78
+ ppid: process.ppid,
79
+ user: os.userInfo().username,
80
+ cwd: process.cwd(),
81
+ argv: process.argv.slice(2).slice(0, 5), // ilk 5 argüman
82
+ action,
83
+ ...data,
84
+ };
85
+
86
+ _writeQueue = _writeQueue.then(() => new Promise((resolve) => {
87
+ try {
88
+ const stream = getStream();
89
+ stream.write(JSON.stringify(entry) + '\n', () => resolve());
90
+ } catch (e) {
91
+ // Audit log hatası uygulamayı kıramamalı
92
+ resolve();
93
+ }
94
+ })).catch(() => {});
95
+
96
+ return _writeQueue;
97
+ }
98
+
99
+ /**
100
+ * Senkron versiyon — kritik olaylarda çağrılabilir (örn: approval deny).
101
+ * Hata durumunda null döner, exception fırlatmaz.
102
+ */
103
+ function logSync(action, data = {}) {
104
+ try {
105
+ ensureDir();
106
+ const entry = {
107
+ ts: new Date().toISOString(),
108
+ pid: process.pid,
109
+ user: os.userInfo().username,
110
+ cwd: process.cwd(),
111
+ action,
112
+ ...data,
113
+ };
114
+ fs.appendFileSync(todayFile(), JSON.stringify(entry) + '\n', 'utf8');
115
+ return true;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Tüm log dosyalarını döner — gün ismine göre sıralı.
123
+ */
124
+ function listLogFiles() {
125
+ ensureDir();
126
+ return fs.readdirSync(AUDIT_DIR)
127
+ .filter(f => f.startsWith('audit-') && f.endsWith('.jsonl'))
128
+ .sort()
129
+ .reverse();
130
+ }
131
+
132
+ /**
133
+ * Belirli bir günün loglarını parse eder.
134
+ */
135
+ function readLog(dateStr) {
136
+ const file = path.join(AUDIT_DIR, `audit-${dateStr}.jsonl`);
137
+ if (!fs.existsSync(file)) return [];
138
+ return fs.readFileSync(file, 'utf8')
139
+ .split('\n')
140
+ .filter(Boolean)
141
+ .map(l => {
142
+ try { return JSON.parse(l); } catch { return null; }
143
+ })
144
+ .filter(Boolean);
145
+ }
146
+
147
+ /**
148
+ * Retention temizliği — RETENTION_DAYS günden eski dosyaları siler.
149
+ */
150
+ function cleanup() {
151
+ ensureDir();
152
+ const cutoff = Date.now() - RETENTION_DAYS * 86400 * 1000;
153
+ let removed = 0;
154
+ for (const file of fs.readdirSync(AUDIT_DIR)) {
155
+ if (!file.startsWith('audit-') || !file.endsWith('.jsonl')) continue;
156
+ const filePath = path.join(AUDIT_DIR, file);
157
+ const stat = fs.statSync(filePath);
158
+ if (stat.mtimeMs < cutoff) {
159
+ try { fs.unlinkSync(filePath); removed++; } catch {}
160
+ }
161
+ }
162
+ return removed;
163
+ }
164
+
165
+ /**
166
+ * Basit istatistik — son 24 saatte hangi actionlar kaç kez?
167
+ */
168
+ function stats24h() {
169
+ const today = new Date().toISOString().slice(0, 10);
170
+ const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
171
+ const entries = [...readLog(today), ...readLog(yesterday)];
172
+ const counts = {};
173
+ for (const e of entries) {
174
+ counts[e.action] = (counts[e.action] || 0) + 1;
175
+ }
176
+ return { total: entries.length, byAction: counts, period: `${yesterday} → ${today}` };
177
+ }
178
+
179
+ /**
180
+ * Flush — bekleyen yazma işlemlerini tamamla. Çıkışta çağrılır.
181
+ */
182
+ async function flush() {
183
+ await _writeQueue;
184
+ if (_stream) {
185
+ return new Promise((resolve) => _stream.end(resolve));
186
+ }
187
+ }
188
+
189
+ module.exports = {
190
+ ACTIONS,
191
+ log,
192
+ logSync,
193
+ listLogFiles,
194
+ readLog,
195
+ cleanup,
196
+ stats24h,
197
+ flush,
198
+ AUDIT_DIR,
199
+ };
File without changes
File without changes
@@ -0,0 +1,136 @@
1
+ /**
2
+ * NatureCo CLI — Branding & Splash
3
+ *
4
+ * Tek kaynaktan tüm logo, banner ve renkleri yönetir.
5
+ * Brand kimliğini koruyalım — her yerde aynı görünüm.
6
+ */
7
+
8
+ const chalk = require('chalk');
9
+ const os = require('os');
10
+
11
+ const COLORS = {
12
+ primary: chalk.hex('#22c55e'), // NatureCo yeşili
13
+ secondary: chalk.hex('#0ea5e9'), // gökyüzü mavisi
14
+ accent: chalk.hex('#f59e0b'), // amber
15
+ muted: chalk.gray,
16
+ danger: chalk.red,
17
+ success: chalk.green,
18
+ bold: chalk.bold,
19
+ };
20
+
21
+ const SMALL_LOGO = [
22
+ ' 🌿 NatureCo',
23
+ ];
24
+
25
+ const FULL_LOGO = [
26
+ '███╗ ██╗ █████╗ ████████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ',
27
+ '████╗ ██║██╔══██╗╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔═══██╗',
28
+ '██╔██╗ ██║███████║ ██║ ██║ ██║██████╔╝█████╗ ██║ ██║██║ ██║',
29
+ '██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗██╔══╝ ██║ ██║██║ ██║',
30
+ '██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████╗╚██████╔╝╚██████╔╝',
31
+ '╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ',
32
+ ];
33
+
34
+ const MASCOT = ` (\\_/)
35
+ (•_•) Hoş geldin!
36
+ />🌿`;
37
+
38
+ const MASCOT_COMPACT = '(\\_/) · (•_•) · />🌿';
39
+
40
+ const TIPS = [
41
+ '🌱 natureco code → Edit & refactor any file in one command',
42
+ '💬 natureco chat "İchigo" → Talk to your default bot',
43
+ '📦 natureco skills install seo-audit → Add new capabilities',
44
+ '🔁 natureco cron add "0 9 * * *" "seo-check" → Schedule recurring jobs',
45
+ '🛡️ natureco doctor → Diagnose setup issues in seconds',
46
+ '🌍 natureco naturehub post "Hello world" → Publish to Nature Hub',
47
+ '🔍 natureco seo audit https://natureco.me → SEO insights, instantly',
48
+ '💰 natureco cost today → See today\'s AI spend',
49
+ ];
50
+
51
+ function pickDailyTip() {
52
+ const day = Math.floor(Date.now() / 86400000);
53
+ return TIPS[day % TIPS.length];
54
+ }
55
+
56
+ /**
57
+ * Kompakt banner — her komut çıktısının üstünde kullanılabilir.
58
+ */
59
+ function banner(opts = {}) {
60
+ const { version, subtitle, userName, botName } = opts;
61
+ const lines = [];
62
+ lines.push('');
63
+ lines.push(COLORS.primary.bold(SMALL_LOGO[0]));
64
+ if (version) {
65
+ const v = COLORS.muted(`v${version}`);
66
+ const subtitleLine = subtitle ? COLORS.secondary(` · ${subtitle}`) : '';
67
+ lines.push(` ${v}${subtitleLine}`);
68
+ }
69
+ lines.push('');
70
+ return lines.join('\n');
71
+ }
72
+
73
+ /**
74
+ * Tam logo — sadece ilk açılışta ve --version gibi özel yerlerde.
75
+ */
76
+ function fullLogo(opts = {}) {
77
+ const { version, tagline } = opts;
78
+ const lines = [];
79
+ for (const ln of FULL_LOGO) lines.push(COLORS.primary(ln));
80
+ lines.push('');
81
+ if (tagline) lines.push(' ' + COLORS.secondary(tagline));
82
+ if (version) lines.push(' ' + COLORS.muted(`v${version} · Node ${process.version.slice(1)} · ${os.platform()}`));
83
+ lines.push('');
84
+ return lines.join('\n');
85
+ }
86
+
87
+ /**
88
+ * Gateway ekranı — komut yoksa gösterilen ana ekran.
89
+ */
90
+ function gatewayScreen({ version, userName, botName, loginStatus }) {
91
+ const lines = [];
92
+ lines.push(COLORS.primary.bold(' 🌿 NatureCo CLI'));
93
+ lines.push(COLORS.muted(` v${version} · OpenClaw\'dan daha güvenli, daha hızlı, daha ucuz`));
94
+ lines.push('');
95
+ lines.push(' ' + COLORS.muted('─'.repeat(64)));
96
+ lines.push(` ${COLORS.muted('👤')} Kullanıcı: ${userName ? COLORS.bold(userName) : COLORS.accent('(setup gerekli)')}`);
97
+ lines.push(` ${COLORS.muted('🤖')} Bot: ${botName ? COLORS.bold(botName) : COLORS.accent('(setup gerekli)')}`);
98
+ lines.push(` ${COLORS.muted('🔐')} Giriş: ${loginStatus === 'ok' ? COLORS.success('✓ aktif') : COLORS.danger('✗ gerekli')}`);
99
+ lines.push(' ' + COLORS.muted('─'.repeat(64)));
100
+ lines.push('');
101
+ lines.push(' ' + COLORS.muted(pickDailyTip()));
102
+ lines.push('');
103
+ lines.push(` ${COLORS.secondary.bold('Hızlı başlangıç:')}`);
104
+ lines.push(` ${COLORS.cyan('natureco chat')} Bot ile sohbet`);
105
+ lines.push(` ${COLORS.cyan('natureco code <file>')} Code agent — dosya oku, düzenle`);
106
+ lines.push(` ${COLORS.cyan('natureco ask "<soru>"')} Tek soru sor`);
107
+ lines.push(` ${COLORS.cyan('natureco doctor')} Sistem teşhisi`);
108
+ lines.push(` ${COLORS.cyan('natureco help')} Tüm komutlar`);
109
+ lines.push('');
110
+ return lines.join('\n');
111
+ }
112
+
113
+ /**
114
+ * İlk kurulum ekranı — setup çalıştırılırken gösterilir.
115
+ */
116
+ function firstRunScreen() {
117
+ const lines = [];
118
+ lines.push(COLORS.primary.bold(SMALL_LOGO[0]));
119
+ lines.push('');
120
+ lines.push(COLORS.secondary.bold(' İlk kurulum hoş geldin!'));
121
+ lines.push(COLORS.muted(' 60 saniyede hazır olalım.\n'));
122
+ return lines.join('\n');
123
+ }
124
+
125
+ module.exports = {
126
+ COLORS,
127
+ SMALL_LOGO,
128
+ FULL_LOGO,
129
+ MASCOT,
130
+ MASCOT_COMPACT,
131
+ pickDailyTip,
132
+ banner,
133
+ fullLogo,
134
+ gatewayScreen,
135
+ firstRunScreen,
136
+ };
File without changes
File without changes
@@ -0,0 +1,360 @@
1
+ /**
2
+ * NatureCo CLI — Cost Tracker & Model Router (Phase 4)
3
+ *
4
+ * Token kullanımını USD maliyete çevirir, model router ile
5
+ * basit soruları ucuz modellere yönlendirir, bütçe aşımında uyarır.
6
+ *
7
+ * OpenClaw: $50-200/ay token patlaması.
8
+ * NatureCo: Hedef $5-15/ay akıllı routing ile.
9
+ *
10
+ * Fiyatlar 2026 Haziran itibarıyladır, değişebilir.
11
+ */
12
+
13
+ // Fiyatlar (USD / 1M token). Provider+model bazlı.
14
+ const PRICING = {
15
+ // MiniMax — pay-as-you-go fiyatları (tahmini, güncellenebilir)
16
+ 'minimax:MiniMax-M3': { input: 0.30, output: 1.20 },
17
+ 'minimax:MiniMax-M2.7': { input: 0.15, output: 0.60 },
18
+ 'minimax:MiniMax-M2.7-highspeed': { input: 0.20, output: 0.80 },
19
+ 'minimax:MiniMax-M2.5': { input: 0.15, output: 0.60 },
20
+ 'minimax:MiniMax-M2.5-highspeed': { input: 0.20, output: 0.80 },
21
+ 'minimax:MiniMax-M2.1': { input: 0.10, output: 0.40 },
22
+ 'minimax:MiniMax-M2.1-highspeed': { input: 0.12, output: 0.50 },
23
+ 'minimax:MiniMax-M2': { input: 0.10, output: 0.40 },
24
+
25
+ // Groq — genelde ücretsiz tier var, indirimli
26
+ 'groq:llama-3.1-8b-instant': { input: 0.05, output: 0.08 },
27
+ 'groq:llama-3.3-70b-versatile': { input: 0.59, output: 0.79 },
28
+ 'groq:mixtral-8x7b-32768': { input: 0.27, output: 0.27 },
29
+ 'groq:llama-3.2-90b-vision-preview': { input: 0.90, output: 0.90 },
30
+
31
+ // OpenAI
32
+ 'openai:gpt-4o': { input: 2.50, output: 10.00 },
33
+ 'openai:gpt-4o-mini': { input: 0.15, output: 0.60 },
34
+ 'openai:gpt-4-turbo': { input: 10.00, output: 30.00 },
35
+ 'openai:o1-mini': { input: 3.00, output: 12.00 },
36
+ 'openai:o1': { input: 15.00, output: 60.00 },
37
+
38
+ // Anthropic
39
+ 'anthropic:claude-sonnet-4-6': { input: 3.00, output: 15.00 },
40
+ 'anthropic:claude-haiku-4-5': { input: 0.80, output: 4.00 },
41
+ 'anthropic:claude-opus-4-5': { input: 15.00, output: 75.00 },
42
+
43
+ // DeepSeek (çok ucuz)
44
+ 'deepseek:deepseek-chat': { input: 0.14, output: 0.28 },
45
+ 'deepseek:deepseek-coder': { input: 0.14, output: 0.28 },
46
+
47
+ // Together AI
48
+ 'together:meta-llama/Llama-3.3-70B-Instruct-Turbo': { input: 0.88, output: 0.88 },
49
+
50
+ // Fireworks
51
+ 'fireworks:accounts/fireworks/models/llama-v3p3-70b-instruct': { input: 0.90, output: 0.90 },
52
+
53
+ // Local (ücretsiz)
54
+ 'ollama:llama3.3': { input: 0, output: 0 },
55
+ 'ollama:qwen2.5-coder': { input: 0, output: 0 },
56
+ 'lmstudio:local': { input: 0, output: 0 },
57
+ };
58
+
59
+ const DEFAULT_PRICING = { input: 1.00, output: 3.00 };
60
+
61
+ function getPricingKey(provider, model) {
62
+ if (!provider || !model) return null;
63
+ const p = provider.toLowerCase().replace(/^https?:\/\/[^/]+\//, '');
64
+ return `${p}:${model}`;
65
+ }
66
+
67
+ function getPricing(provider, model) {
68
+ const key = getPricingKey(provider, model);
69
+ if (key && PRICING[key]) return PRICING[key];
70
+ // Provider'ı bul, ilk modelini kullan
71
+ const prefix = `${provider.toLowerCase()}:`;
72
+ for (const [k, v] of Object.entries(PRICING)) {
73
+ if (k.startsWith(prefix)) return v;
74
+ }
75
+ return DEFAULT_PRICING;
76
+ }
77
+
78
+ /**
79
+ * Token kullanımını USD'ye çevir.
80
+ * @param {object} usage - { input: number, output: number }
81
+ * @param {string} provider
82
+ * @param {string} model
83
+ * @returns {number} USD
84
+ */
85
+ function calculateCost(usage, provider, model) {
86
+ const pricing = getPricing(provider, model);
87
+ const inputCost = (usage.input || 0) / 1_000_000 * pricing.input;
88
+ const outputCost = (usage.output || 0) / 1_000_000 * pricing.output;
89
+ return inputCost + outputCost;
90
+ }
91
+
92
+ /**
93
+ * Model router — görev tipine göre en uygun modeli öner.
94
+ *
95
+ * Karmaşıklık seviyesi:
96
+ * - "simple": Basit soru, kısa cevap, sınıflandırma
97
+ * - "medium": Genel sohbet, doküman özetleme
98
+ * - "complex": Kod yazma, mimari karar, çok adımlı reasoning
99
+ * - "creative": Yaratıcı yazı, hikaye, içerik üretimi
100
+ */
101
+ const ROUTING = {
102
+ simple: {
103
+ description: 'Basit sorular — sınıflandırma, kısa cevaplar',
104
+ models: [
105
+ { provider: 'groq', model: 'llama-3.1-8b-instant', reason: 'En ucuz, hızlı, çoğu basit görev için yeterli' },
106
+ { provider: 'groq', model: 'llama-3.3-70b-versatile', reason: 'Yedek: daha kaliteli ama ucuz' },
107
+ { provider: 'anthropic', model: 'claude-haiku-4-5', reason: 'Yüksek kalite gerekiyorsa' },
108
+ ],
109
+ },
110
+ medium: {
111
+ description: 'Genel sohbet, özetleme, doküman analizi',
112
+ models: [
113
+ { provider: 'groq', model: 'llama-3.3-70b-versatile', reason: 'Dengeli fiyat/kalite' },
114
+ { provider: 'anthropic', model: 'claude-haiku-4-5', reason: 'Daha kaliteli cevap' },
115
+ { provider: 'openai', model: 'gpt-4o-mini', reason: 'OpenAI istiyorsan' },
116
+ ],
117
+ },
118
+ complex: {
119
+ description: 'Kod yazma, mimari, çok adımlı reasoning',
120
+ models: [
121
+ { provider: 'groq', model: 'llama-3.3-70b-versatile', reason: 'Hızlı, yeterince akıllı' },
122
+ { provider: 'anthropic', model: 'claude-sonnet-4-6', reason: 'Kod için en iyi kalite' },
123
+ { provider: 'openai', model: 'gpt-4o', reason: 'OpenAI istiyorsan' },
124
+ ],
125
+ },
126
+ creative: {
127
+ description: 'Yaratıcı yazı, hikaye, içerik üretimi',
128
+ models: [
129
+ { provider: 'anthropic', model: 'claude-sonnet-4-6', reason: 'Yaratıcı yazıda güçlü' },
130
+ { provider: 'openai', model: 'gpt-4o', reason: 'OpenAI yaratıcı kalitesi' },
131
+ { provider: 'groq', model: 'llama-3.3-70b-versatile', reason: 'Ücretsiz denemek için' },
132
+ ],
133
+ },
134
+ };
135
+
136
+ /**
137
+ * Basit karmaşıklık tahmini — prompt içeriğine bakarak.
138
+ */
139
+ function estimateComplexity(prompt) {
140
+ if (!prompt) return 'simple';
141
+ const text = prompt.toLowerCase();
142
+ const len = text.length;
143
+
144
+ // Code işaretleri
145
+ const codeIndicators = (text.match(/```|function|class|import|const |let |var |=>|\{|\}/g) || []).length;
146
+ if (codeIndicators >= 3) return 'complex';
147
+
148
+ // Karmaşık soru işaretleri
149
+ const complexKeywords = ['mimari', 'tasarla', 'analiz', 'karşılaştır', 'açıkla', 'optimize et', 'refactor', 'debug', 'neden', 'nasıl çalışır'];
150
+ if (complexKeywords.some(k => text.includes(k))) return 'complex';
151
+
152
+ // Yaratıcı işaretler
153
+ const creativeKeywords = ['yaz', 'hikaye', 'şiir', 'blog', 'içerik', 'makale', 'yaratıcı', 'senaryo'];
154
+ if (creativeKeywords.some(k => text.includes(k)) && len > 100) return 'creative';
155
+
156
+ // Uzun metin → medium
157
+ if (len > 500) return 'medium';
158
+
159
+ return 'simple';
160
+ }
161
+
162
+ /**
163
+ * Bir prompt için önerilen modeli döner.
164
+ */
165
+ function suggestModel(prompt, options = {}) {
166
+ const { forceComplexity, currentProvider, currentModel } = options;
167
+
168
+ // Mevcut model zaten pahalıysa ve görev basitse, uyar
169
+ const complexity = forceComplexity || estimateComplexity(prompt);
170
+ const route = ROUTING[complexity];
171
+
172
+ if (!route) return null;
173
+
174
+ // Mevcut model routing listesinde mi?
175
+ if (currentProvider && currentModel) {
176
+ const matched = route.models.find(m => m.provider === currentProvider.toLowerCase() && m.model === currentModel);
177
+ if (matched) {
178
+ return { complexity, ...matched, optimal: true };
179
+ }
180
+ }
181
+
182
+ // İlk öneriyi döndür
183
+ return { complexity, ...route.models[0], optimal: false };
184
+ }
185
+
186
+ /**
187
+ * Bir kullanım kaydını cost-tracker'a ekle.
188
+ */
189
+ const fs = require('fs');
190
+ const path = require('path');
191
+ const os = require('os');
192
+
193
+ const COST_FILE = path.join(os.homedir(), '.natureco', 'cost-tracking.json');
194
+ const BUDGET_CONFIG_FILE = path.join(os.homedir(), '.natureco', 'budget-config.json');
195
+
196
+ const DEFAULT_BUDGET = {
197
+ dailyLimit: 5.00, // USD/gün
198
+ monthlyLimit: 100.00, // USD/ay
199
+ warnAt: 0.75, // %75'inde uyar
200
+ downgradeAt: 0.90, // %90'ında otomatik downgrade
201
+ };
202
+
203
+ function loadBudget() {
204
+ try {
205
+ if (fs.existsSync(BUDGET_CONFIG_FILE)) {
206
+ return { ...DEFAULT_BUDGET, ...JSON.parse(fs.readFileSync(BUDGET_CONFIG_FILE, 'utf8')) };
207
+ }
208
+ } catch {}
209
+ return { ...DEFAULT_BUDGET };
210
+ }
211
+
212
+ function saveBudget(budget) {
213
+ const dir = path.dirname(BUDGET_CONFIG_FILE);
214
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
215
+ fs.writeFileSync(BUDGET_CONFIG_FILE, JSON.stringify(budget, null, 2), 'utf8');
216
+ }
217
+
218
+ function loadCosts() {
219
+ try {
220
+ if (fs.existsSync(COST_FILE)) {
221
+ return JSON.parse(fs.readFileSync(COST_FILE, 'utf8'));
222
+ }
223
+ } catch {}
224
+ return { entries: [] };
225
+ }
226
+
227
+ function saveCosts(data) {
228
+ const dir = path.dirname(COST_FILE);
229
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
230
+ fs.writeFileSync(COST_FILE, JSON.stringify(data, null, 2), 'utf8');
231
+ }
232
+
233
+ /**
234
+ * Yeni bir kullanım kaydı ekle.
235
+ */
236
+ function recordUsage({ provider, model, input, output, sessionId, command }) {
237
+ const data = loadCosts();
238
+ const cost = calculateCost({ input, output }, provider, model);
239
+ const entry = {
240
+ ts: new Date().toISOString(),
241
+ provider,
242
+ model,
243
+ input: input || 0,
244
+ output: output || 0,
245
+ cost,
246
+ sessionId: sessionId || null,
247
+ command: command || null,
248
+ };
249
+ data.entries.push(entry);
250
+ // Maksimum 10.000 entry tut (eski dosyaları rotate et)
251
+ if (data.entries.length > 10000) {
252
+ data.entries = data.entries.slice(-10000);
253
+ }
254
+ saveCosts(data);
255
+ return entry;
256
+ }
257
+
258
+ /**
259
+ * Belirli bir dönem için toplam maliyet.
260
+ */
261
+ function totalForPeriod(period = 'today') {
262
+ const data = loadCosts();
263
+ const now = new Date();
264
+ let cutoff;
265
+
266
+ switch (period) {
267
+ case 'today':
268
+ cutoff = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
269
+ break;
270
+ case 'week':
271
+ cutoff = now.getTime() - 7 * 86400000;
272
+ break;
273
+ case 'month':
274
+ cutoff = new Date(now.getFullYear(), now.getMonth(), 1).getTime();
275
+ break;
276
+ case 'all':
277
+ cutoff = 0;
278
+ break;
279
+ default:
280
+ cutoff = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
281
+ }
282
+
283
+ const filtered = data.entries.filter(e => new Date(e.ts).getTime() >= cutoff);
284
+ let totalCost = 0;
285
+ let totalInput = 0;
286
+ let totalOutput = 0;
287
+ const byProvider = {};
288
+ const byModel = {};
289
+
290
+ for (const e of filtered) {
291
+ totalCost += e.cost;
292
+ totalInput += e.input;
293
+ totalOutput += e.output;
294
+ byProvider[e.provider] = (byProvider[e.provider] || 0) + e.cost;
295
+ byModel[`${e.provider}:${e.model}`] = (byModel[`${e.provider}:${e.model}`] || 0) + e.cost;
296
+ }
297
+
298
+ return {
299
+ period,
300
+ entries: filtered.length,
301
+ totalCost,
302
+ totalInput,
303
+ totalOutput,
304
+ byProvider,
305
+ byModel,
306
+ topModel: Object.entries(byModel).sort((a, b) => b[1] - a[1])[0]?.[0] || null,
307
+ };
308
+ }
309
+
310
+ /**
311
+ * Bütçe kontrolü — limit aşıldı mı?
312
+ */
313
+ function checkBudget() {
314
+ const budget = loadBudget();
315
+ const today = totalForPeriod('today');
316
+ const month = totalForPeriod('month');
317
+
318
+ const dailyUsage = today.totalCost / budget.dailyLimit;
319
+ const monthlyUsage = month.totalCost / budget.monthlyLimit;
320
+
321
+ return {
322
+ daily: {
323
+ spent: today.totalCost,
324
+ limit: budget.dailyLimit,
325
+ usage: dailyUsage,
326
+ exceeded: dailyUsage >= 1.0,
327
+ warning: dailyUsage >= budget.warnAt,
328
+ },
329
+ monthly: {
330
+ spent: month.totalCost,
331
+ limit: budget.monthlyLimit,
332
+ usage: monthlyUsage,
333
+ exceeded: monthlyUsage >= 1.0,
334
+ warning: monthlyUsage >= budget.warnAt,
335
+ },
336
+ shouldDowngrade: dailyUsage >= budget.downgradeAt || monthlyUsage >= budget.downgradeAt,
337
+ };
338
+ }
339
+
340
+ function formatUSD(amount) {
341
+ if (amount === 0) return '$0.00';
342
+ if (amount < 0.01) return `$${(amount * 100).toFixed(2)}¢`;
343
+ return `$${amount.toFixed(4)}`;
344
+ }
345
+
346
+ module.exports = {
347
+ PRICING,
348
+ ROUTING,
349
+ DEFAULT_BUDGET,
350
+ getPricing,
351
+ calculateCost,
352
+ estimateComplexity,
353
+ suggestModel,
354
+ loadBudget,
355
+ saveBudget,
356
+ recordUsage,
357
+ totalForPeriod,
358
+ checkBudget,
359
+ formatUSD,
360
+ };
package/src/utils/cron.js CHANGED
File without changes