natureco-cli 2.23.32 → 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.
- package/AUDIT.md +178 -0
- package/CHANGELOG.md +422 -0
- package/DEPLOY_v2.0.0.md +400 -0
- package/README.md +159 -1
- package/bin/natureco.js +170 -8
- package/package.json +43 -11
- package/skills/code-review/SKILL.md +0 -0
- package/skills/summarize/SKILL.md +0 -0
- package/skills/translate/SKILL.md +0 -0
- package/src/commands/acp.js +0 -0
- package/src/commands/admin-rpc.js +0 -0
- package/src/commands/agent.js +0 -0
- package/src/commands/agents.js +0 -0
- package/src/commands/approvals.js +0 -0
- package/src/commands/ask.js +0 -0
- package/src/commands/audit.js +209 -0
- package/src/commands/backup.js +0 -0
- package/src/commands/bonjour.js +0 -0
- package/src/commands/bots.js +0 -0
- package/src/commands/browser.js +0 -0
- package/src/commands/capability.js +0 -0
- package/src/commands/channels.js +0 -0
- package/src/commands/chat.js +0 -0
- package/src/commands/clawbot.js +0 -0
- package/src/commands/clickclack.js +0 -0
- package/src/commands/code.js +0 -0
- package/src/commands/commands.js +0 -0
- package/src/commands/commitments.js +0 -0
- package/src/commands/completion.js +0 -0
- package/src/commands/config.js +0 -0
- package/src/commands/configure.js +0 -0
- package/src/commands/cost.js +210 -0
- package/src/commands/crestodian.js +0 -0
- package/src/commands/cron.js +0 -0
- package/src/commands/daemon.js +0 -0
- package/src/commands/dashboard.js +126 -45
- package/src/commands/device-pair.js +0 -0
- package/src/commands/devices.js +0 -0
- package/src/commands/directory.js +0 -0
- package/src/commands/discord.js +0 -0
- package/src/commands/dns.js +0 -0
- package/src/commands/docs.js +0 -0
- package/src/commands/doctor.js +134 -15
- package/src/commands/exec-policy.js +0 -0
- package/src/commands/gateway-server.js +0 -0
- package/src/commands/gateway.js +0 -0
- package/src/commands/git.js +0 -0
- package/src/commands/health.js +0 -0
- package/src/commands/help.js +0 -0
- package/src/commands/hooks.js +0 -0
- package/src/commands/imessage.js +0 -0
- package/src/commands/infer.js +0 -0
- package/src/commands/init.js +0 -0
- package/src/commands/irc.js +0 -0
- package/src/commands/login.js +0 -0
- package/src/commands/logout.js +0 -0
- package/src/commands/logs.js +0 -0
- package/src/commands/mattermost.js +0 -0
- package/src/commands/mcp.js +0 -0
- package/src/commands/medium.js +206 -0
- package/src/commands/memory-cmd.js +0 -0
- package/src/commands/memory.js +0 -0
- package/src/commands/message.js +0 -0
- package/src/commands/migrate.js +0 -0
- package/src/commands/models.js +0 -0
- package/src/commands/naturehub.js +156 -0
- package/src/commands/node.js +0 -0
- package/src/commands/nodes.js +0 -0
- package/src/commands/oc-path.js +0 -0
- package/src/commands/onboard.js +0 -0
- package/src/commands/open-prose.js +0 -0
- package/src/commands/pairing.js +0 -0
- package/src/commands/path.js +0 -0
- package/src/commands/plugins.js +0 -0
- package/src/commands/policy.js +0 -0
- package/src/commands/proxy.js +0 -0
- package/src/commands/qr.js +0 -0
- package/src/commands/reset.js +0 -0
- package/src/commands/run.js +0 -0
- package/src/commands/sandbox.js +0 -0
- package/src/commands/secrets.js +0 -0
- package/src/commands/security.js +0 -0
- package/src/commands/seo.js +268 -0
- package/src/commands/sessions.js +0 -0
- package/src/commands/setup.js +6 -6
- package/src/commands/signal.js +0 -0
- package/src/commands/skills.js +82 -1
- package/src/commands/slack.js +0 -0
- package/src/commands/sms.js +0 -0
- package/src/commands/status.js +0 -0
- package/src/commands/system.js +0 -0
- package/src/commands/tasks.js +0 -0
- package/src/commands/team.js +171 -0
- package/src/commands/telegram.js +0 -0
- package/src/commands/terminal.js +0 -0
- package/src/commands/thread-ownership.js +0 -0
- package/src/commands/transcripts.js +0 -0
- package/src/commands/tui.js +0 -0
- package/src/commands/ultrareview.js +0 -0
- package/src/commands/uninstall.js +0 -0
- package/src/commands/update.js +0 -0
- package/src/commands/voice.js +0 -0
- package/src/commands/vydra.js +0 -0
- package/src/commands/web-fetch.js +0 -0
- package/src/commands/webhooks.js +0 -0
- package/src/commands/whatsapp.js +0 -0
- package/src/commands/wiki.js +0 -0
- package/src/commands/workboard.js +0 -0
- package/src/commands/xp.js +194 -0
- package/src/tools/audio_understanding.js +0 -0
- package/src/tools/bash.js +0 -0
- package/src/tools/browser.js +0 -0
- package/src/tools/canvas.js +0 -0
- package/src/tools/document_extract.js +0 -0
- package/src/tools/duckduckgo.js +0 -0
- package/src/tools/exa_search.js +0 -0
- package/src/tools/filesystem.js +0 -0
- package/src/tools/firecrawl.js +0 -0
- package/src/tools/git.js +0 -0
- package/src/tools/http.js +0 -0
- package/src/tools/image_generation.js +0 -0
- package/src/tools/list_dir.js +0 -0
- package/src/tools/llm_task.js +43 -11
- package/src/tools/media_understanding.js +0 -0
- package/src/tools/music_generation.js +0 -0
- package/src/tools/parallel_search.js +0 -0
- package/src/tools/phone_control.js +0 -0
- package/src/tools/phone_control_enhanced.js +0 -0
- package/src/tools/read_file.js +0 -0
- package/src/tools/searxng.js +0 -0
- package/src/tools/speech_to_text.js +0 -0
- package/src/tools/text_to_speech.js +0 -0
- package/src/tools/thread_ownership.js +0 -0
- package/src/tools/video_generation.js +0 -0
- package/src/tools/web_readability.js +0 -0
- package/src/tools/web_search.js +0 -0
- package/src/tools/write_file.js +0 -0
- package/src/utils/agents-md.js +0 -0
- package/src/utils/agents.js +0 -0
- package/src/utils/api.js +5 -1
- package/src/utils/approvals.js +0 -0
- package/src/utils/audit.js +199 -0
- package/src/utils/background.js +0 -0
- package/src/utils/baileys.js +0 -0
- package/src/utils/branding.js +136 -0
- package/src/utils/commands.js +0 -0
- package/src/utils/config.js +0 -0
- package/src/utils/cost-tracker.js +360 -0
- package/src/utils/cron.js +0 -0
- package/src/utils/dashboard-server.js +284 -0
- package/src/utils/errors.js +0 -0
- package/src/utils/format.js +7 -10
- package/src/utils/gateway-ws.js +0 -0
- package/src/utils/headless.js +0 -0
- package/src/utils/history.js +0 -0
- package/src/utils/hooks.js +0 -0
- package/src/utils/inquirer-wrapper.js +0 -0
- package/src/utils/mcp-client.js +0 -0
- package/src/utils/mcp.js +0 -0
- package/src/utils/memory.js +0 -0
- package/src/utils/parallel-tools.js +0 -0
- package/src/utils/path-utils.js +0 -0
- package/src/utils/pattern-detector.js +314 -0
- package/src/utils/plugin-registry.js +0 -0
- package/src/utils/secret-scanner.js +204 -0
- package/src/utils/secrets.js +0 -0
- package/src/utils/sessions.js +0 -0
- package/src/utils/skills.js +0 -0
- package/src/utils/sub-agent.js +6 -0
- package/src/utils/token-budget.js +0 -0
- package/src/utils/tool-adapter.js +0 -0
- package/src/utils/tool-runner.js +0 -0
- package/src/utils/tui.js +750 -0
- package/src/utils/web-fetch.js +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* natureco cost — Maliyet takibi ve bütçe yönetimi (Phase 4)
|
|
3
|
+
*
|
|
4
|
+
* Kullanım:
|
|
5
|
+
* natureco cost Bugünün maliyetini göster
|
|
6
|
+
* natureco cost today Bugünkü maliyet
|
|
7
|
+
* natureco cost week Bu haftaki maliyet
|
|
8
|
+
* natureco cost month Bu ayki maliyet
|
|
9
|
+
* natureco cost all Tüm zamanlar
|
|
10
|
+
* natureco cost budget Bütçe durumu ve uyarılar
|
|
11
|
+
* natureco cost set <key> <val> Bütçe ayarla (dailyLimit 5, monthlyLimit 100, warnAt 0.75)
|
|
12
|
+
* natureco cost model <prompt> Bir prompt için önerilen modeli göster
|
|
13
|
+
* natureco cost prices Tüm fiyatları listele
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const chalk = require('chalk');
|
|
17
|
+
const tui = require('../utils/tui');
|
|
18
|
+
const cost = require('../utils/cost-tracker');
|
|
19
|
+
const audit = require('../utils/audit');
|
|
20
|
+
|
|
21
|
+
function showPeriod(period) {
|
|
22
|
+
const data = cost.totalForPeriod(period);
|
|
23
|
+
const icon = { today: '📅', week: '📆', month: '🗓️', all: '♾️ ' }[period] || '💰';
|
|
24
|
+
console.log(tui.styled(`\n ${icon} Maliyet Raporu · ${period.toUpperCase()}`, { color: tui.PALETTE.primary, bold: true }));
|
|
25
|
+
console.log(tui.styled(' ' + '─'.repeat(56), { color: tui.PALETTE.border }));
|
|
26
|
+
|
|
27
|
+
// Üst metrik kartı
|
|
28
|
+
const cardWidth = 54;
|
|
29
|
+
const cardLines = [];
|
|
30
|
+
cardLines.push(tui.styled(' ╭' + '─'.repeat(cardWidth) + '╮', { color: tui.PALETTE.border }));
|
|
31
|
+
cardLines.push(tui.styled(' │', { color: tui.PALETTE.border }) + ' ' + tui.C.muted('Toplam maliyet:') + ' ' + tui.styled(cost.formatUSD(data.totalCost).padStart(12), { color: tui.PALETTE.primary, bold: true }) + ' ' + tui.styled('│', { color: tui.PALETTE.border }));
|
|
32
|
+
cardLines.push(tui.styled(' │', { color: tui.PALETTE.border }) + ' ' + tui.C.muted('Toplam token:') + ' ' + tui.C.text((data.totalInput + data.totalOutput).toLocaleString().padStart(8) + ` (${data.totalInput.toLocaleString()} in / ${data.totalOutput.toLocaleString()} out)`) + ' ' + tui.styled('│', { color: tui.PALETTE.border }));
|
|
33
|
+
cardLines.push(tui.styled(' │', { color: tui.PALETTE.border }) + ' ' + tui.C.muted('Çağrı sayısı:') + ' ' + tui.C.text(String(data.entries).padStart(8)) + ' ' + tui.styled('│', { color: tui.PALETTE.border }));
|
|
34
|
+
cardLines.push(tui.styled(' ╰' + '─'.repeat(cardWidth) + '╯', { color: tui.PALETTE.border }));
|
|
35
|
+
console.log(cardLines.join('\n'));
|
|
36
|
+
|
|
37
|
+
if (Object.keys(data.byProvider).length > 0) {
|
|
38
|
+
console.log('\n' + tui.styled(' 💵 Provider Bazlı Maliyet', { color: tui.PALETTE.secondary, bold: true }));
|
|
39
|
+
const providerRows = Object.entries(data.byProvider)
|
|
40
|
+
.sort((a, b) => b[1] - a[1])
|
|
41
|
+
.map(([provider, amount]) => ({
|
|
42
|
+
provider,
|
|
43
|
+
amount: cost.formatUSD(amount),
|
|
44
|
+
pct: tui.progressBar(amount / Math.max(0.01, data.totalCost), 1, { width: 20, showPercent: false }),
|
|
45
|
+
}));
|
|
46
|
+
console.log('\n' + tui.table(providerRows, [
|
|
47
|
+
{ key: 'provider', label: 'Provider', minWidth: 12 },
|
|
48
|
+
{ key: 'amount', label: 'Maliyet', minWidth: 10, render: r => tui.C.brand(r.amount) },
|
|
49
|
+
{ key: 'pct', label: 'Dağılım', minWidth: 20, render: r => tui.styled(r.pct, { color: tui.PALETTE.primary }) },
|
|
50
|
+
], { borderStyle: 'round', zebra: true }));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (Object.keys(data.byModel).length > 0) {
|
|
54
|
+
console.log('\n' + tui.styled(' 🤖 Model Bazlı Maliyet (Top 5)', { color: tui.PALETTE.accent, bold: true }));
|
|
55
|
+
const sortedModels = Object.entries(data.byModel).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
56
|
+
const modelRows = sortedModels.map(([model, amount]) => ({
|
|
57
|
+
model,
|
|
58
|
+
amount: cost.formatUSD(amount),
|
|
59
|
+
}));
|
|
60
|
+
console.log('\n' + tui.table(modelRows, [
|
|
61
|
+
{ key: 'model', label: 'Model', minWidth: 40, render: r => tui.C.muted(r.model) },
|
|
62
|
+
{ key: 'amount', label: 'Maliyet', minWidth: 10, render: r => tui.C.brand(r.amount) },
|
|
63
|
+
], { borderStyle: 'round', zebra: true }));
|
|
64
|
+
}
|
|
65
|
+
console.log('');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function showBudget() {
|
|
69
|
+
const status = cost.checkBudget();
|
|
70
|
+
const budget = cost.loadBudget();
|
|
71
|
+
|
|
72
|
+
console.log('\n' + tui.styled(' 🛡️ Bütçe Durumu', { color: tui.PALETTE.primary, bold: true }));
|
|
73
|
+
console.log(tui.styled(' ' + '─'.repeat(56), { color: tui.PALETTE.border }));
|
|
74
|
+
|
|
75
|
+
// Günlük
|
|
76
|
+
const dailyBar = tui.progressBar(status.daily.usage, 1, {
|
|
77
|
+
width: 25, showPercent: true,
|
|
78
|
+
fillChar: status.daily.exceeded ? '▓' : status.daily.warning ? '▒' : '█',
|
|
79
|
+
});
|
|
80
|
+
const dailyColor = status.daily.exceeded ? tui.PALETTE.danger : status.daily.warning ? tui.PALETTE.warning : tui.PALETTE.success;
|
|
81
|
+
console.log(`\n ${tui.C.muted('Günlük limit')} ${tui.C.brand(cost.formatUSD(budget.dailyLimit).padStart(10))} ${tui.styled(dailyBar, { color: dailyColor })}`);
|
|
82
|
+
if (status.daily.exceeded) console.log(' ' + tui.styled('⚠️ Günlük limit aşıldı!', { color: tui.PALETTE.danger, bold: true }));
|
|
83
|
+
else if (status.daily.warning) console.log(' ' + tui.styled(`⚠️ %${(budget.warnAt * 100)} eşiğine yaklaşıldı`, { color: tui.PALETTE.warning }));
|
|
84
|
+
|
|
85
|
+
console.log('');
|
|
86
|
+
|
|
87
|
+
// Aylık
|
|
88
|
+
const monthlyBar = tui.progressBar(status.monthly.usage, 1, {
|
|
89
|
+
width: 25, showPercent: true,
|
|
90
|
+
fillChar: status.monthly.exceeded ? '▓' : status.monthly.warning ? '▒' : '█',
|
|
91
|
+
});
|
|
92
|
+
const monthlyColor = status.monthly.exceeded ? tui.PALETTE.danger : status.monthly.warning ? tui.PALETTE.warning : tui.PALETTE.success;
|
|
93
|
+
console.log(` ${tui.C.muted('Aylık limit')} ${tui.C.brand(cost.formatUSD(budget.monthlyLimit).padStart(10))} ${tui.styled(monthlyBar, { color: monthlyColor })}`);
|
|
94
|
+
if (status.monthly.exceeded) console.log(' ' + tui.styled('⚠️ Aylık limit aşıldı!', { color: tui.PALETTE.danger, bold: true }));
|
|
95
|
+
else if (status.monthly.warning) console.log(' ' + tui.styled(`⚠️ %${(budget.warnAt * 100)} eşiğine yaklaşıldı`, { color: tui.PALETTE.warning }));
|
|
96
|
+
|
|
97
|
+
if (status.shouldDowngrade) {
|
|
98
|
+
console.log('\n' + tui.styled(' ⬇️ Otomatik downgrade önerilir — basit soruları ucuz modele yönlendir.', { color: tui.PALETTE.warning, bold: true }));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log('\n' + tui.C.muted(' Ayarlamak için: ') + tui.C.brand('natureco cost set <key> <value>'));
|
|
102
|
+
console.log('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setBudget(args) {
|
|
106
|
+
const [key, value] = args;
|
|
107
|
+
if (!key || value === undefined) {
|
|
108
|
+
console.log(chalk.red('\n Kullanım: natureco cost set <key> <value>\n'));
|
|
109
|
+
console.log(chalk.gray(' Anahtarlar: dailyLimit, monthlyLimit, warnAt, downgradeAt'));
|
|
110
|
+
console.log(chalk.gray(' Örnek: natureco cost set dailyLimit 3.00'));
|
|
111
|
+
console.log('');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const budget = cost.loadBudget();
|
|
115
|
+
const num = parseFloat(value);
|
|
116
|
+
if (isNaN(num) && key !== 'preset') {
|
|
117
|
+
console.log(chalk.red(`\n Geçersiz sayı: ${value}\n`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
budget[key] = num;
|
|
121
|
+
cost.saveBudget(budget);
|
|
122
|
+
console.log(chalk.green(`\n ✓ ${key} = ${value}\n`));
|
|
123
|
+
audit.log(audit.ACTIONS.CONFIG_CHANGE, { source: 'cost', key, value });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function suggestModel(args) {
|
|
127
|
+
const prompt = args.join(' ');
|
|
128
|
+
if (!prompt) {
|
|
129
|
+
console.log(chalk.red('\n Kullanım: natureco cost model "<prompt>"\n'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const suggestion = cost.suggestModel(prompt);
|
|
133
|
+
if (!suggestion) return;
|
|
134
|
+
|
|
135
|
+
console.log(chalk.bold('\n 🎯 Model Önerisi\n'));
|
|
136
|
+
console.log(chalk.gray(' Prompt karmaşıklığı: ') + chalk.cyan(suggestion.complexity));
|
|
137
|
+
console.log(chalk.gray(` ${cost.ROUTING[suggestion.complexity].description}\n`));
|
|
138
|
+
console.log(chalk.gray(' Önerilen model: ') + chalk.bold(`${suggestion.provider}:${suggestion.model}`));
|
|
139
|
+
console.log(chalk.gray(' Neden: ') + chalk.gray(suggestion.reason));
|
|
140
|
+
|
|
141
|
+
const pricing = cost.getPricing(suggestion.provider, suggestion.model);
|
|
142
|
+
console.log(chalk.gray(' Fiyat: ') + chalk.cyan(`${cost.formatUSD(pricing.input)} in / ${cost.formatUSD(pricing.output)} out (per 1M)`));
|
|
143
|
+
|
|
144
|
+
if (suggestion.optimal) {
|
|
145
|
+
console.log(chalk.green('\n ✓ Mevcut model zaten optimal.\n'));
|
|
146
|
+
} else {
|
|
147
|
+
console.log(chalk.yellow('\n ⚠️ Mevcut model optimal değil. Değiştirmek için:'));
|
|
148
|
+
console.log(chalk.cyan(` natureco config set providerUrl <url>`));
|
|
149
|
+
console.log(chalk.cyan(` natureco config set providerModel ${suggestion.model}`));
|
|
150
|
+
console.log('');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function showPrices() {
|
|
155
|
+
console.log(chalk.bold('\n 💵 Model Fiyatları (USD / 1M token)\n'));
|
|
156
|
+
console.log(chalk.gray(' ' + '─'.repeat(60)));
|
|
157
|
+
|
|
158
|
+
// Provider'lara göre grupla
|
|
159
|
+
const grouped = {};
|
|
160
|
+
for (const [key, price] of Object.entries(cost.PRICING)) {
|
|
161
|
+
const [provider] = key.split(':');
|
|
162
|
+
if (!grouped[provider]) grouped[provider] = [];
|
|
163
|
+
grouped[provider].push({ model: key.slice(provider.length + 1), ...price });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const [provider, models] of Object.entries(grouped)) {
|
|
167
|
+
console.log(chalk.bold(`\n ${provider.toUpperCase()}`));
|
|
168
|
+
for (const m of models) {
|
|
169
|
+
const inPrice = m.input === 0 ? chalk.green('ücretsiz') : cost.formatUSD(m.input);
|
|
170
|
+
const outPrice = m.output === 0 ? chalk.green('ücretsiz') : cost.formatUSD(m.output);
|
|
171
|
+
console.log(` ${m.model.padEnd(40)} ${inPrice.padStart(10)} in ${outPrice.padStart(10)} out`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
console.log('');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function cost_cmd(args) {
|
|
178
|
+
const [action, ...params] = args || [];
|
|
179
|
+
|
|
180
|
+
if (!action || action === 'today') {
|
|
181
|
+
showPeriod('today');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (action === 'week') { showPeriod('week'); return; }
|
|
186
|
+
if (action === 'month') { showPeriod('month'); return; }
|
|
187
|
+
if (action === 'all') { showPeriod('all'); return; }
|
|
188
|
+
|
|
189
|
+
if (action === 'budget') { showBudget(); return; }
|
|
190
|
+
|
|
191
|
+
if (action === 'set') { setBudget(params); return; }
|
|
192
|
+
|
|
193
|
+
if (action === 'model') { suggestModel(params); return; }
|
|
194
|
+
|
|
195
|
+
if (action === 'prices') { showPrices(); return; }
|
|
196
|
+
|
|
197
|
+
// Yardım
|
|
198
|
+
console.log(chalk.yellow('\n Kullanım:'));
|
|
199
|
+
console.log(chalk.gray(' natureco cost Bugünün maliyeti'));
|
|
200
|
+
console.log(chalk.gray(' natureco cost week Bu hafta'));
|
|
201
|
+
console.log(chalk.gray(' natureco cost month Bu ay'));
|
|
202
|
+
console.log(chalk.gray(' natureco cost all Tüm zamanlar'));
|
|
203
|
+
console.log(chalk.gray(' natureco cost budget Bütçe durumu'));
|
|
204
|
+
console.log(chalk.gray(' natureco cost set <k> <v> Bütçe ayarla'));
|
|
205
|
+
console.log(chalk.gray(' natureco cost model "<p>" Model önerisi'));
|
|
206
|
+
console.log(chalk.gray(' natureco cost prices Fiyat listesi'));
|
|
207
|
+
console.log('');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = cost_cmd;
|
|
File without changes
|
package/src/commands/cron.js
CHANGED
|
File without changes
|
package/src/commands/daemon.js
CHANGED
|
File without changes
|
|
@@ -1,69 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* natureco dashboard — Local web dashboard (Phase 5)
|
|
3
|
+
*
|
|
4
|
+
* Kullanım:
|
|
5
|
+
* natureco dashboard Dashboard'u başlat ve tarayıcıda aç
|
|
6
|
+
* natureco dashboard start Sadece başlat (arka plan)
|
|
7
|
+
* natureco dashboard status Çalışıyor mu kontrol et
|
|
8
|
+
* natureco dashboard url URL'i göster
|
|
9
|
+
* natureco dashboard stop Durdur (PID file'dan)
|
|
10
|
+
*
|
|
11
|
+
* Phase 5 — Vanilla JS, framework yok, port 7421
|
|
12
|
+
*/
|
|
13
|
+
|
|
1
14
|
const chalk = require('chalk');
|
|
2
|
-
const
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const os = require('os');
|
|
3
18
|
const net = require('net');
|
|
19
|
+
const { exec } = require('child_process');
|
|
20
|
+
const dashboardServer = require('../utils/dashboard-server');
|
|
4
21
|
|
|
5
|
-
const
|
|
22
|
+
const DASHBOARD_PORT = dashboardServer.PORT;
|
|
23
|
+
const DASHBOARD_URL = `http://${dashboardServer.HOST}:${DASHBOARD_PORT}`;
|
|
24
|
+
const PID_FILE = path.join(os.homedir(), '.natureco', 'dashboard.pid');
|
|
6
25
|
|
|
7
|
-
function
|
|
26
|
+
function isPortInUse(port, host = '127.0.0.1') {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const socket = new net.Socket();
|
|
29
|
+
socket.setTimeout(1000);
|
|
30
|
+
socket.on('connect', () => { socket.destroy(); resolve(true); });
|
|
31
|
+
socket.on('timeout', () => { socket.destroy(); resolve(false); });
|
|
32
|
+
socket.on('error', () => resolve(false));
|
|
33
|
+
socket.connect(port, host);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getRunningPid() {
|
|
8
38
|
try {
|
|
9
|
-
|
|
39
|
+
if (fs.existsSync(PID_FILE)) {
|
|
40
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
41
|
+
// Process çalışıyor mu?
|
|
42
|
+
try { process.kill(pid, 0); return pid; } catch { return null; }
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
10
47
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
48
|
+
async function cmdStart(openBrowser = true) {
|
|
49
|
+
const inUse = await isPortInUse(DASHBOARD_PORT);
|
|
50
|
+
const existingPid = getRunningPid();
|
|
14
51
|
|
|
15
|
-
|
|
16
|
-
console.log(chalk.
|
|
17
|
-
|
|
18
|
-
|
|
52
|
+
if (inUse || existingPid) {
|
|
53
|
+
console.log(chalk.green(`\n ✓ Dashboard zaten çalışıyor: ${DASHBOARD_URL}`));
|
|
54
|
+
console.log(chalk.gray(` PID: ${existingPid || 'unknown'}`));
|
|
55
|
+
if (openBrowser) openInBrowser(DASHBOARD_URL);
|
|
56
|
+
return;
|
|
19
57
|
}
|
|
20
|
-
}
|
|
21
58
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const platform = process.platform;
|
|
59
|
+
console.log(chalk.cyan(`\n 🌿 NatureCo Dashboard başlatılıyor...\n`));
|
|
60
|
+
const server = dashboardServer.startServer(DASHBOARD_PORT);
|
|
25
61
|
|
|
26
|
-
|
|
62
|
+
// PID kaydet
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
try {
|
|
65
|
+
fs.writeFileSync(PID_FILE, String(process.pid), 'utf8');
|
|
66
|
+
} catch {}
|
|
67
|
+
}, 100);
|
|
27
68
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
exec('start ' + DASHBOARD_URL);
|
|
31
|
-
} else if (platform === 'darwin') {
|
|
32
|
-
exec('open ' + DASHBOARD_URL);
|
|
33
|
-
} else {
|
|
34
|
-
exec('xdg-open ' + DASHBOARD_URL);
|
|
35
|
-
}
|
|
36
|
-
} catch (e) {
|
|
37
|
-
console.log(chalk.yellow(' Could not open browser. Visit: ' + DASHBOARD_URL + '\n'));
|
|
69
|
+
if (openBrowser) {
|
|
70
|
+
setTimeout(() => openInBrowser(DASHBOARD_URL), 500);
|
|
38
71
|
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function cmdStatus() {
|
|
42
|
-
const socket = new net.Socket();
|
|
43
|
-
const timeout = 2000;
|
|
44
72
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
// Graceful shutdown
|
|
74
|
+
process.on('SIGINT', () => {
|
|
75
|
+
console.log(chalk.yellow('\n ⏹ Dashboard durduruluyor...'));
|
|
76
|
+
try { fs.unlinkSync(PID_FILE); } catch {}
|
|
77
|
+
server.close(() => process.exit(0));
|
|
50
78
|
});
|
|
79
|
+
}
|
|
51
80
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
81
|
+
function openInBrowser(url) {
|
|
82
|
+
const platform = process.platform;
|
|
83
|
+
try {
|
|
84
|
+
if (platform === 'win32') exec(`start ${url}`);
|
|
85
|
+
else if (platform === 'darwin') exec(`open ${url}`);
|
|
86
|
+
else exec(`xdg-open ${url}`);
|
|
87
|
+
} catch {}
|
|
88
|
+
}
|
|
56
89
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
90
|
+
async function cmdStatus() {
|
|
91
|
+
const inUse = await isPortInUse(DASHBOARD_PORT);
|
|
92
|
+
const pid = getRunningPid();
|
|
61
93
|
|
|
62
|
-
|
|
94
|
+
if (inUse || pid) {
|
|
95
|
+
console.log(chalk.green(`\n ✓ Dashboard çalışıyor`));
|
|
96
|
+
console.log(chalk.gray(` URL: ${DASHBOARD_URL}`));
|
|
97
|
+
if (pid) console.log(chalk.gray(` PID: ${pid}`));
|
|
98
|
+
} else {
|
|
99
|
+
console.log(chalk.yellow(`\n ⏸ Dashboard çalışmıyor`));
|
|
100
|
+
console.log(chalk.gray(` Başlatmak için: natureco dashboard`));
|
|
101
|
+
}
|
|
102
|
+
console.log('');
|
|
63
103
|
}
|
|
64
104
|
|
|
65
105
|
function cmdUrl() {
|
|
66
106
|
console.log(DASHBOARD_URL);
|
|
67
107
|
}
|
|
68
108
|
|
|
109
|
+
function cmdStop() {
|
|
110
|
+
const pid = getRunningPid();
|
|
111
|
+
if (!pid) {
|
|
112
|
+
console.log(chalk.yellow('\n Dashboard zaten çalışmıyor.\n'));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
process.kill(pid, 'SIGTERM');
|
|
117
|
+
try { fs.unlinkSync(PID_FILE); } catch {}
|
|
118
|
+
console.log(chalk.green(`\n ✓ Dashboard durduruldu (PID ${pid})\n`));
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.log(chalk.red(`\n ❌ Durdurulamadı: ${e.message}\n`));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function dashboard(params) {
|
|
125
|
+
try {
|
|
126
|
+
// Hem string hem array kabul et (eski/yeni bin uyumluluğu)
|
|
127
|
+
const action = Array.isArray(params) ? params[0] : params;
|
|
128
|
+
const allParams = Array.isArray(params) ? params : [params];
|
|
129
|
+
|
|
130
|
+
if (!action || action === 'open' || action === 'start') {
|
|
131
|
+
await cmdStart(action !== 'start');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (action === 'status') { await cmdStatus(); return; }
|
|
135
|
+
if (action === 'url') { cmdUrl(); return; }
|
|
136
|
+
if (action === 'stop') { cmdStop(); return; }
|
|
137
|
+
|
|
138
|
+
console.log(chalk.yellow('\n Kullanım:'));
|
|
139
|
+
console.log(chalk.gray(' natureco dashboard Başlat ve tarayıcıda aç'));
|
|
140
|
+
console.log(chalk.gray(' natureco dashboard start Sadece başlat'));
|
|
141
|
+
console.log(chalk.gray(' natureco dashboard status Çalışıyor mu?'));
|
|
142
|
+
console.log(chalk.gray(' natureco dashboard stop Durdur'));
|
|
143
|
+
console.log(chalk.gray(' natureco dashboard url URL göster'));
|
|
144
|
+
console.log('');
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.log(chalk.red(`\n Dashboard error: ${err.message}\n`));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
69
150
|
module.exports = dashboard;
|
|
File without changes
|
package/src/commands/devices.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/commands/discord.js
CHANGED
|
File without changes
|
package/src/commands/dns.js
CHANGED
|
File without changes
|
package/src/commands/docs.js
CHANGED
|
File without changes
|
package/src/commands/doctor.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const F = require('../utils/format');
|
|
3
|
+
const tui = require('../utils/tui');
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const os = require('os');
|
|
7
|
+
const audit = require('../utils/audit');
|
|
8
|
+
const secrets = require('../utils/secret-scanner');
|
|
6
9
|
|
|
7
10
|
const BASE_DIR = path.join(os.homedir(), '.natureco');
|
|
8
11
|
const CONFIG_FILE = path.join(BASE_DIR, 'config.json');
|
|
@@ -11,8 +14,13 @@ const CHECKS = [
|
|
|
11
14
|
{ name: 'configExists', label: 'Config file exists' },
|
|
12
15
|
{ name: 'nodeVersion', label: 'Node.js version >= 18' },
|
|
13
16
|
{ name: 'npmPackages', label: 'Required npm packages installed' },
|
|
14
|
-
{ name: 'diskSpace', label: 'Sufficient disk space' },
|
|
17
|
+
{ name: 'diskSpace', label: 'Sufficient disk space (>500 MB)' },
|
|
15
18
|
{ name: 'writePermission', label: 'Write permission on ~/.natureco' },
|
|
19
|
+
{ name: 'apiKeyValid', label: 'API key format valid' },
|
|
20
|
+
{ name: 'providerReachable', label: 'Provider API reachable' },
|
|
21
|
+
{ name: 'dataDirs', label: 'All data directories exist' },
|
|
22
|
+
{ name: 'auditLog', label: 'Audit log directory writable' },
|
|
23
|
+
{ name: 'secretsClean', label: 'No secrets in current directory' },
|
|
16
24
|
];
|
|
17
25
|
|
|
18
26
|
function doctor(params) {
|
|
@@ -58,30 +66,53 @@ function cmdCheck(name) {
|
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
function cmdRun() {
|
|
61
|
-
F.header('System Doctor');
|
|
69
|
+
F.header('System Doctor · Tüm Sistem Kontrolleri', { icon: '🩺' });
|
|
62
70
|
|
|
63
71
|
const rows = [];
|
|
64
72
|
let passed = 0;
|
|
65
73
|
let failed = 0;
|
|
74
|
+
const startTime = Date.now();
|
|
66
75
|
|
|
67
76
|
for (const check of CHECKS) {
|
|
68
77
|
const result = runCheck(check.name);
|
|
69
|
-
rows.push(
|
|
70
|
-
check.label,
|
|
71
|
-
result.pass
|
|
72
|
-
result.message,
|
|
73
|
-
|
|
78
|
+
rows.push({
|
|
79
|
+
check: check.label,
|
|
80
|
+
status: result.pass,
|
|
81
|
+
message: result.message,
|
|
82
|
+
});
|
|
74
83
|
if (result.pass) passed++; else failed++;
|
|
75
84
|
}
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
// Yeni TUI tablo
|
|
87
|
+
console.log('\n' + tui.table(rows, [
|
|
88
|
+
{ key: 'check', label: 'Kontrol', minWidth: 28 },
|
|
89
|
+
{
|
|
90
|
+
key: 'status', label: 'Durum', minWidth: 9,
|
|
91
|
+
render: r => r.status
|
|
92
|
+
? tui.styled(' ✓ PASS ', { bg: tui.PALETTE.success, color: '#000000', bold: true })
|
|
93
|
+
: tui.styled(' ✗ FAIL ', { bg: tui.PALETTE.danger, color: '#000000', bold: true }),
|
|
94
|
+
},
|
|
95
|
+
{ key: 'message', label: 'Mesaj', minWidth: 20 },
|
|
96
|
+
], { borderStyle: 'round', zebra: true }));
|
|
78
97
|
|
|
79
98
|
const total = passed + failed;
|
|
80
|
-
|
|
99
|
+
const duration = Date.now() - startTime;
|
|
100
|
+
|
|
101
|
+
// Özet kartı
|
|
102
|
+
console.log('\n' + tui.box(60, 5, {
|
|
103
|
+
title: 'Özet',
|
|
104
|
+
borderColor: failed > 0 ? tui.PALETTE.warning : tui.PALETTE.success,
|
|
105
|
+
}).split('\n').map((line, i) => {
|
|
106
|
+
if (i === 2) return line.replace(' '.repeat(58), ` ${tui.C.text(`${passed}/${total} kontrol geçti`)} · ${tui.C.muted(duration + 'ms')}`);
|
|
107
|
+
return line;
|
|
108
|
+
}).join('\n'));
|
|
81
109
|
|
|
82
110
|
if (failed > 0) {
|
|
83
|
-
|
|
111
|
+
console.log('\n' + tui.C.amber(' ⚠️ Bazı kontroller başarısız. Detay için: ') + tui.C.brand('natureco doctor check <name>'));
|
|
112
|
+
} else {
|
|
113
|
+
console.log('\n' + tui.C.green(' ✨ Tüm kontroller geçti! Sistem sağlıklı.'));
|
|
84
114
|
}
|
|
115
|
+
console.log('');
|
|
85
116
|
}
|
|
86
117
|
|
|
87
118
|
function runCheck(name) {
|
|
@@ -111,15 +142,29 @@ function runCheck(name) {
|
|
|
111
142
|
}
|
|
112
143
|
|
|
113
144
|
case 'diskSpace': {
|
|
145
|
+
// Gerçek disk alanı (cross-platform)
|
|
114
146
|
try {
|
|
115
|
-
const
|
|
116
|
-
|
|
147
|
+
const { execSync } = require('child_process');
|
|
148
|
+
let freeGB = null;
|
|
149
|
+
if (process.platform === 'darwin' || process.platform === 'linux') {
|
|
150
|
+
// df -k ~/.natureco | tail -1 | awk '{print $4}'
|
|
151
|
+
const out = execSync(`df -k ${JSON.stringify(BASE_DIR)} | tail -1`).toString().trim();
|
|
152
|
+
const parts = out.split(/\s+/);
|
|
153
|
+
const freeKB = parseInt(parts[3], 10);
|
|
154
|
+
if (!isNaN(freeKB)) freeGB = freeKB / 1024 / 1024;
|
|
155
|
+
} else if (process.platform === 'win32') {
|
|
156
|
+
const out = execSync(`powershell -NoProfile -Command "(Get-PSDrive ${BASE_DIR[0]}).Free / 1GB"`).toString().trim();
|
|
157
|
+
freeGB = parseFloat(out);
|
|
158
|
+
}
|
|
159
|
+
if (freeGB === null || isNaN(freeGB)) {
|
|
160
|
+
return { pass: true, message: 'Unable to determine disk space' };
|
|
161
|
+
}
|
|
117
162
|
return {
|
|
118
163
|
pass: freeGB > 0.5,
|
|
119
|
-
message: freeGB > 0.5 ? `${freeGB.toFixed(1)} GB free` :
|
|
164
|
+
message: freeGB > 0.5 ? `${freeGB.toFixed(1)} GB free` : `Sadece ${(freeGB * 1024).toFixed(0)} MB kaldı — gerekli: 500 MB`,
|
|
120
165
|
};
|
|
121
|
-
} catch {
|
|
122
|
-
return { pass: true, message: 'Unable to check' };
|
|
166
|
+
} catch (e) {
|
|
167
|
+
return { pass: true, message: 'Unable to check disk space' };
|
|
123
168
|
}
|
|
124
169
|
}
|
|
125
170
|
|
|
@@ -135,6 +180,80 @@ function runCheck(name) {
|
|
|
135
180
|
}
|
|
136
181
|
}
|
|
137
182
|
|
|
183
|
+
case 'apiKeyValid': {
|
|
184
|
+
try {
|
|
185
|
+
if (!fs.existsSync(CONFIG_FILE)) return { pass: false, message: 'Config missing — run `natureco setup`' };
|
|
186
|
+
const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
187
|
+
const key = cfg.providerApiKey || cfg.apiKey;
|
|
188
|
+
if (!key) return { pass: false, message: 'API key tanımlı değil' };
|
|
189
|
+
if (key.length < 10) return { pass: false, message: 'API key çok kısa — yanlış kopyalanmış olabilir' };
|
|
190
|
+
return { pass: true, message: `Key uzunluğu: ${key.length} karakter` };
|
|
191
|
+
} catch (e) {
|
|
192
|
+
return { pass: false, message: e.message };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
case 'providerReachable': {
|
|
197
|
+
try {
|
|
198
|
+
if (!fs.existsSync(CONFIG_FILE)) return { pass: false, message: 'Config missing' };
|
|
199
|
+
const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
200
|
+
const url = cfg.providerUrl;
|
|
201
|
+
if (!url) return { pass: false, message: 'Provider URL tanımlı değil' };
|
|
202
|
+
// URL format geçerli mi?
|
|
203
|
+
const parsed = new URL(url);
|
|
204
|
+
return { pass: true, message: `URL geçerli: ${parsed.host}` };
|
|
205
|
+
} catch (e) {
|
|
206
|
+
return { pass: false, message: e.message };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
case 'dataDirs': {
|
|
211
|
+
try {
|
|
212
|
+
if (!fs.existsSync(BASE_DIR)) fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
213
|
+
const REQUIRED = ['sources', 'concepts', 'cache', 'skills', 'memory', 'sessions', 'backups', 'hooks', 'audit'];
|
|
214
|
+
const missing = REQUIRED.filter(d => !fs.existsSync(path.join(BASE_DIR, d)));
|
|
215
|
+
if (missing.length === 0) {
|
|
216
|
+
return { pass: true, message: `${REQUIRED.length} dizin hazır` };
|
|
217
|
+
}
|
|
218
|
+
// Otomatik oluştur
|
|
219
|
+
for (const d of missing) {
|
|
220
|
+
try { fs.mkdirSync(path.join(BASE_DIR, d), { recursive: true }); } catch {}
|
|
221
|
+
}
|
|
222
|
+
return { pass: true, message: `Eksik dizinler oluşturuldu: ${missing.join(', ')}` };
|
|
223
|
+
} catch (e) {
|
|
224
|
+
return { pass: false, message: e.message };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
case 'auditLog': {
|
|
229
|
+
try {
|
|
230
|
+
// Audit log yazma testi
|
|
231
|
+
audit.logSync(audit.ACTIONS.INFO, { source: 'doctor', check: 'auditLog' });
|
|
232
|
+
const files = audit.listLogFiles();
|
|
233
|
+
return { pass: true, message: `${files.length} log dosyası, en son: ${files[0] || 'yok'}` };
|
|
234
|
+
} catch (e) {
|
|
235
|
+
return { pass: false, message: e.message };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case 'secretsClean': {
|
|
240
|
+
try {
|
|
241
|
+
// Mevcut çalışma dizinini tara — secret var mı?
|
|
242
|
+
const findings = secrets.scanDir(process.cwd());
|
|
243
|
+
const critical = findings.filter(f => f.severity === 'critical' || f.severity === 'high');
|
|
244
|
+
if (critical.length === 0) {
|
|
245
|
+
return { pass: true, message: 'Çalışma dizininde secret bulunamadı ✓' };
|
|
246
|
+
}
|
|
247
|
+
const sample = critical.slice(0, 3).map(f => `${f.type}@${path.basename(f.file || '?')}`).join(', ');
|
|
248
|
+
return {
|
|
249
|
+
pass: false,
|
|
250
|
+
message: `${critical.length} potansiyel secret: ${sample}${critical.length > 3 ? '...' : ''}`,
|
|
251
|
+
};
|
|
252
|
+
} catch (e) {
|
|
253
|
+
return { pass: false, message: e.message };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
138
257
|
default:
|
|
139
258
|
return { pass: false, message: 'Unknown check' };
|
|
140
259
|
}
|
|
File without changes
|
|
File without changes
|
package/src/commands/gateway.js
CHANGED
|
File without changes
|
package/src/commands/git.js
CHANGED
|
File without changes
|
package/src/commands/health.js
CHANGED
|
File without changes
|
package/src/commands/help.js
CHANGED
|
File without changes
|
package/src/commands/hooks.js
CHANGED
|
File without changes
|
package/src/commands/imessage.js
CHANGED
|
File without changes
|
package/src/commands/infer.js
CHANGED
|
File without changes
|
package/src/commands/init.js
CHANGED
|
File without changes
|
package/src/commands/irc.js
CHANGED
|
File without changes
|
package/src/commands/login.js
CHANGED
|
File without changes
|
package/src/commands/logout.js
CHANGED
|
File without changes
|
package/src/commands/logs.js
CHANGED
|
File without changes
|