natureco-cli 2.23.27 → 2.23.29
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/bin/natureco.js +68 -6
- package/package.json +10 -6
- package/src/commands/channels.js +94 -4
- package/src/commands/chat.js +11 -25
- package/src/commands/code.js +12 -11
- package/src/commands/config.js +111 -68
- package/src/commands/doctor.js +121 -16
- package/src/commands/gateway-server.js +35 -21
- package/src/commands/gateway.js +11 -20
- package/src/commands/help.js +6 -0
- package/src/commands/imessage.js +55 -0
- package/src/commands/irc.js +70 -0
- package/src/commands/mattermost.js +62 -0
- package/src/commands/message.js +24 -4
- package/src/commands/models.js +584 -216
- package/src/commands/plugins.js +415 -172
- package/src/commands/security.js +149 -1
- package/src/commands/setup.js +1 -3
- package/src/commands/signal.js +66 -0
- package/src/commands/skills.js +20 -29
- package/src/commands/sms.js +64 -0
- package/src/commands/tasks.js +328 -79
- package/src/commands/webhooks.js +79 -0
- package/src/commands/whatsapp.js +7 -21
- package/src/tools/bash.js +63 -29
- package/src/utils/api.js +3 -20
- package/src/utils/approvals.js +297 -0
- package/src/utils/background.js +223 -66
- package/src/utils/baileys.js +21 -0
- package/src/utils/config.js +141 -10
- package/src/utils/errors.js +148 -0
- package/src/utils/inquirer-wrapper.js +1 -2
- package/src/utils/path-utils.js +13 -13
- package/src/utils/plugin-registry.js +238 -0
- package/src/utils/secrets.js +177 -0
- package/src/utils/skills.js +10 -23
package/src/commands/models.js
CHANGED
|
@@ -1,216 +1,584 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
const PROVIDER_MODELS = {
|
|
6
|
-
'api.groq.com': [
|
|
7
|
-
{ id: 'llama-3.3-70b-versatile', label: 'Llama 3.3 70B Versatile
|
|
8
|
-
{ id: 'llama-3.1-8b-instant', label: 'Llama 3.1 8B Instant
|
|
9
|
-
{ id: 'llama-3.
|
|
10
|
-
{ id: '
|
|
11
|
-
{ id: '
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
{ id: '
|
|
15
|
-
{ id: '
|
|
16
|
-
{ id: '
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{ id: '
|
|
21
|
-
{ id: '
|
|
22
|
-
{ id: '
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{ id: '
|
|
26
|
-
{ id: '
|
|
27
|
-
{ id: '
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { getConfig, saveConfig } = require('../utils/config');
|
|
3
|
+
const { NatureCoError, handleError } = require('../utils/errors');
|
|
4
|
+
|
|
5
|
+
const PROVIDER_MODELS = {
|
|
6
|
+
'api.groq.com': [
|
|
7
|
+
{ id: 'llama-3.3-70b-versatile', label: 'Llama 3.3 70B Versatile', features: ['tool', 'vision'] },
|
|
8
|
+
{ id: 'llama-3.1-8b-instant', label: 'Llama 3.1 8B Instant', features: ['tool'] },
|
|
9
|
+
{ id: 'llama-3.2-90b-vision-preview', label: 'Llama 3.2 90B Vision', features: ['tool', 'vision'] },
|
|
10
|
+
{ id: 'llama-3.1-70b-versatile', label: 'Llama 3.1 70B Versatile', features: ['tool'] },
|
|
11
|
+
{ id: 'mixtral-8x7b-32768', label: 'Mixtral 8x7B', features: ['tool'] },
|
|
12
|
+
{ id: 'gemma2-9b-it', label: 'Gemma 2 9B', features: [] },
|
|
13
|
+
{ id: 'llama-guard-3-8b', label: 'Llama Guard 3 8B', features: [] },
|
|
14
|
+
{ id: 'llama-3.2-1b-preview', label: 'Llama 3.2 1B Preview', features: [] },
|
|
15
|
+
{ id: 'llama-3.2-3b-preview', label: 'Llama 3.2 3B Preview', features: [] },
|
|
16
|
+
{ id: 'distil-whisper-large-v3-en', label: 'Distil Whisper v3', features: ['audio'] },
|
|
17
|
+
],
|
|
18
|
+
'api.openai.com': [
|
|
19
|
+
{ id: 'gpt-4o', label: 'GPT-4o', features: ['tool', 'vision'] },
|
|
20
|
+
{ id: 'gpt-4o-mini', label: 'GPT-4o Mini', features: ['tool', 'vision'] },
|
|
21
|
+
{ id: 'gpt-4-turbo', label: 'GPT-4 Turbo', features: ['tool', 'vision'] },
|
|
22
|
+
{ id: 'gpt-4', label: 'GPT-4', features: ['tool'] },
|
|
23
|
+
{ id: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo', features: ['tool'] },
|
|
24
|
+
{ id: 'o1-preview', label: 'o1 Preview', features: [] },
|
|
25
|
+
{ id: 'o1-mini', label: 'o1 Mini', features: [] },
|
|
26
|
+
{ id: 'dall-e-3', label: 'DALL-E 3', features: ['image'] },
|
|
27
|
+
{ id: 'tts-1', label: 'TTS-1', features: ['audio'] },
|
|
28
|
+
{ id: 'whisper-1', label: 'Whisper-1', features: ['audio'] },
|
|
29
|
+
],
|
|
30
|
+
'api.anthropic.com': [
|
|
31
|
+
{ id: 'claude-opus-4-7', label: 'Claude Opus 4.7', features: ['tool', 'vision'] },
|
|
32
|
+
{ id: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', features: ['tool', 'vision'] },
|
|
33
|
+
{ id: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', features: ['tool', 'vision'] },
|
|
34
|
+
{ id: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet', features: ['tool', 'vision'] },
|
|
35
|
+
{ id: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku', features: ['tool', 'vision'] },
|
|
36
|
+
],
|
|
37
|
+
'api.together.xyz': [
|
|
38
|
+
{ id: 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo', label: 'Llama 3.1 70B Turbo', features: ['tool'] },
|
|
39
|
+
{ id: 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', label: 'Llama 3.1 8B Turbo', features: ['tool'] },
|
|
40
|
+
{ id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', label: 'Llama 3.2 90B Vision', features: ['tool', 'vision'] },
|
|
41
|
+
{ id: 'mistralai/Mixtral-8x7B-Instruct-v0.1', label: 'Mixtral 8x7B', features: ['tool'] },
|
|
42
|
+
{ id: 'deepseek-ai/deepseek-coder-33b-instruct', label: 'DeepSeek Coder 33B', features: [] },
|
|
43
|
+
],
|
|
44
|
+
'api.deepseek.com': [
|
|
45
|
+
{ id: 'deepseek-chat', label: 'DeepSeek Chat', features: ['tool'] },
|
|
46
|
+
{ id: 'deepseek-coder', label: 'DeepSeek Coder', features: ['tool'] },
|
|
47
|
+
{ id: 'deepseek-reasoner', label: 'DeepSeek Reasoner', features: [] },
|
|
48
|
+
],
|
|
49
|
+
'api.mistral.ai': [
|
|
50
|
+
{ id: 'mistral-large-latest', label: 'Mistral Large', features: ['tool'] },
|
|
51
|
+
{ id: 'mistral-medium-latest', label: 'Mistral Medium', features: ['tool'] },
|
|
52
|
+
{ id: 'mistral-small-latest', label: 'Mistral Small', features: ['tool'] },
|
|
53
|
+
{ id: 'codestral-latest', label: 'Codestral', features: ['tool'] },
|
|
54
|
+
],
|
|
55
|
+
'api.perplexity.ai': [
|
|
56
|
+
{ id: 'sonar-pro', label: 'Sonar Pro', features: ['tool'] },
|
|
57
|
+
{ id: 'sonar', label: 'Sonar', features: ['tool'] },
|
|
58
|
+
{ id: 'sonar-reasoning-pro', label: 'Sonar Reasoning Pro', features: [] },
|
|
59
|
+
],
|
|
60
|
+
'api.x.ai': [
|
|
61
|
+
{ id: 'grok-beta', label: 'Grok Beta', features: ['tool'] },
|
|
62
|
+
{ id: 'grok-vision-beta', label: 'Grok Vision Beta', features: ['tool', 'vision'] },
|
|
63
|
+
],
|
|
64
|
+
'api.deepinfra.com': [
|
|
65
|
+
{ id: 'meta-llama/Meta-Llama-3.1-70B-Instruct', label: 'Llama 3.1 70B Instruct', features: ['tool'] },
|
|
66
|
+
{ id: 'meta-llama/Meta-Llama-3.1-8B-Instruct', label: 'Llama 3.1 8B Instruct', features: ['tool'] },
|
|
67
|
+
{ id: 'mistralai/Mixtral-8x22B-Instruct-v0.1', label: 'Mixtral 8x22B', features: ['tool'] },
|
|
68
|
+
],
|
|
69
|
+
'fireworks.ai': [
|
|
70
|
+
{ id: 'accounts/fireworks/models/llama-v3p1-70b-instruct', label: 'Llama 3.1 70B Instruct', features: ['tool'] },
|
|
71
|
+
{ id: 'accounts/fireworks/models/llama-v3p1-8b-instruct', label: 'Llama 3.1 8B Instruct', features: ['tool'] },
|
|
72
|
+
],
|
|
73
|
+
'natureco.me': [
|
|
74
|
+
{ id: 'natureco-default', label: 'NatureCo Default (otomatik)', features: ['tool'] },
|
|
75
|
+
{ id: 'natureco-fast', label: 'NatureCo Fast (hızlı)', features: ['tool'] },
|
|
76
|
+
{ id: 'natureco-reasoner', label: 'NatureCo Reasoner', features: [] },
|
|
77
|
+
],
|
|
78
|
+
'openrouter.ai': [
|
|
79
|
+
{ id: 'openrouter/auto', label: 'OpenRouter Auto (otomatik seçim)', features: ['tool', 'vision'] },
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const PROVIDER_API_PATTERNS = [
|
|
84
|
+
{ match: 'openai.com', modelsEndpoint: 'https://api.openai.com/v1/models', type: 'openai' },
|
|
85
|
+
{ match: 'groq.com', modelsEndpoint: null, type: 'groq' },
|
|
86
|
+
{ match: 'anthropic.com', modelsEndpoint: null, type: 'anthropic' },
|
|
87
|
+
{ match: 'together.xyz', modelsEndpoint: null, type: 'openai' },
|
|
88
|
+
{ match: 'deepseek.com', modelsEndpoint: 'https://api.deepseek.com/v1/models', type: 'openai' },
|
|
89
|
+
{ match: 'mistral.ai', modelsEndpoint: 'https://api.mistral.ai/v1/models', type: 'openai' },
|
|
90
|
+
{ match: 'perplexity.ai', modelsEndpoint: 'https://api.perplexity.ai/v1/models', type: 'openai' },
|
|
91
|
+
{ match: 'x.ai', modelsEndpoint: 'https://api.x.ai/v1/models', type: 'openai' },
|
|
92
|
+
{ match: 'deepinfra.com', modelsEndpoint: 'https://api.deepinfra.com/v1/models', type: 'openai' },
|
|
93
|
+
{ match: 'fireworks.ai', modelsEndpoint: 'https://api.fireworks.ai/v1/models', type: 'openai' },
|
|
94
|
+
{ match: 'openrouter.ai', modelsEndpoint: 'https://openrouter.ai/api/v1/models', type: 'openrouter' },
|
|
95
|
+
{ match: 'natureco.me', modelsEndpoint: null, type: 'natureco' },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const FEATURE_ICONS = {
|
|
99
|
+
tool: '🔧',
|
|
100
|
+
vision: '👁',
|
|
101
|
+
audio: '🎤',
|
|
102
|
+
image: '🎨',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
async function models(args) {
|
|
106
|
+
const [action, ...params] = (args || []);
|
|
107
|
+
|
|
108
|
+
if (!action || action === 'list' || action === 'status') {
|
|
109
|
+
const opts = parseFlags(params);
|
|
110
|
+
return listModels(opts);
|
|
111
|
+
}
|
|
112
|
+
if (action === 'set') return setModel(params[0]);
|
|
113
|
+
if (action === 'scan') {
|
|
114
|
+
const opts = parseFlags(params);
|
|
115
|
+
return scanModels(opts);
|
|
116
|
+
}
|
|
117
|
+
if (action === 'aliases') return manageAliases(params);
|
|
118
|
+
if (action === 'fallbacks') return manageFallbacks(params);
|
|
119
|
+
|
|
120
|
+
console.log(chalk.red(`\n ❌ Bilinmeyen komut: ${action}\n`));
|
|
121
|
+
console.log(chalk.gray(' Kullanım: natureco models [list|set|scan|aliases|fallbacks]\n'));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function parseFlags(params) {
|
|
126
|
+
return {
|
|
127
|
+
refresh: params.includes('--refresh') || params.includes('-r'),
|
|
128
|
+
probe: params.includes('--probe') || params.includes('-p'),
|
|
129
|
+
json: params.includes('--json') || params.includes('-j'),
|
|
130
|
+
all: params.includes('--all') || params.includes('-a'),
|
|
131
|
+
provider: extractFlag(params, '--provider'),
|
|
132
|
+
timeout: parseInt(extractFlag(params, '--timeout') || '10000', 10),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function extractFlag(params, name) {
|
|
137
|
+
const idx = params.indexOf(name);
|
|
138
|
+
return idx >= 0 && idx + 1 < params.length ? params[idx + 1] : null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function listModels(opts) {
|
|
142
|
+
const config = getConfig();
|
|
143
|
+
const providerUrl = config.providerUrl || '';
|
|
144
|
+
const currentModel = config.providerModel || '';
|
|
145
|
+
const fallbackModel = config.fallbackModel || '';
|
|
146
|
+
const count = opts.refresh ? 50 : 0;
|
|
147
|
+
|
|
148
|
+
const providerHost = providerUrl.replace('https://', '').split('/')[0] || 'yapılandırılmamış';
|
|
149
|
+
let liveModels = [];
|
|
150
|
+
|
|
151
|
+
if (opts.refresh) {
|
|
152
|
+
const endpoint = findModelsEndpoint(providerUrl);
|
|
153
|
+
if (endpoint) {
|
|
154
|
+
console.log(chalk.gray('\n Canlı modeller taranıyor...\n'));
|
|
155
|
+
try {
|
|
156
|
+
liveModels = await fetchLiveModels(endpoint, config.providerApiKey, opts);
|
|
157
|
+
if (liveModels.length > 0) {
|
|
158
|
+
console.log(chalk.gray(` ${liveModels.length} model bulundu\n`));
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
console.log(chalk.yellow(` ⚠ ${err.message}\n`));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (opts.json) {
|
|
167
|
+
const data = {
|
|
168
|
+
provider: providerHost,
|
|
169
|
+
currentModel,
|
|
170
|
+
fallbackModel,
|
|
171
|
+
models: liveModels.length > 0 ? liveModels : getKnownModels(providerUrl),
|
|
172
|
+
liveModels: liveModels.length > 0,
|
|
173
|
+
};
|
|
174
|
+
console.log(JSON.stringify(data, null, 2));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
console.log('');
|
|
179
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
180
|
+
console.log(chalk.cyan.bold('\n Model Yapılandırması\n'));
|
|
181
|
+
console.log(chalk.gray(' Provider : ') + chalk.white(providerHost));
|
|
182
|
+
console.log(chalk.gray(' Aktif : ') + chalk.cyan(currentModel || 'ayarlanmamış'));
|
|
183
|
+
if (fallbackModel) {
|
|
184
|
+
console.log(chalk.gray(' Yedek : ') + chalk.yellow(fallbackModel));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (config.modelAliases && Object.keys(config.modelAliases).length > 0) {
|
|
188
|
+
console.log(chalk.gray(' Takma ad : ') + chalk.white(
|
|
189
|
+
Object.entries(config.modelAliases).map(([k, v]) => `${k}→${v}`).join(', ')
|
|
190
|
+
));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (liveModels.length > 0) {
|
|
194
|
+
console.log(chalk.cyan.bold(`\n Canlı Modeller (${liveModels.length})\n`));
|
|
195
|
+
liveModels.slice(0, count || 30).forEach(m => {
|
|
196
|
+
const active = m.id === currentModel ? chalk.green(' ← aktif') : '';
|
|
197
|
+
const fallback = m.id === fallbackModel ? chalk.yellow(' ← yedek') : '';
|
|
198
|
+
console.log(chalk.white(` ${m.id}`) + active + fallback);
|
|
199
|
+
});
|
|
200
|
+
if (liveModels.length > 30 && !count) {
|
|
201
|
+
console.log(chalk.gray(` ... ve ${liveModels.length - 30} model daha (--refresh ile tümü)`));
|
|
202
|
+
}
|
|
203
|
+
} else if (!opts.refresh) {
|
|
204
|
+
const knownModels = getKnownModels(providerUrl);
|
|
205
|
+
if (knownModels.length > 0) {
|
|
206
|
+
console.log(chalk.cyan.bold('\n Bilinen Modeller\n'));
|
|
207
|
+
knownModels.forEach(m => {
|
|
208
|
+
const active = m.id === currentModel ? chalk.green(' ← aktif') : '';
|
|
209
|
+
const fallback = m.id === fallbackModel ? chalk.yellow(' ← yedek') : '';
|
|
210
|
+
const featureIcons = (m.features || []).map(f => FEATURE_ICONS[f] || '').join(' ');
|
|
211
|
+
console.log(chalk.white(` ${m.id}`) + active + fallback);
|
|
212
|
+
console.log(chalk.gray(` ${m.label}`) + (featureIcons ? ` ${featureIcons}` : ''));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (opts.probe && config.providerUrl) {
|
|
218
|
+
console.log(chalk.cyan.bold('\n Canlı Sınama\n'));
|
|
219
|
+
await probeProvider(config.providerUrl, config.providerApiKey, currentModel, opts);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log('');
|
|
223
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
224
|
+
console.log(chalk.gray(' Değiştirmek : ') + chalk.cyan('natureco models set <model-id>'));
|
|
225
|
+
console.log(chalk.gray(' Taramak : ') + chalk.cyan('natureco models scan'));
|
|
226
|
+
console.log(chalk.gray(' Yedek model : ') + chalk.cyan('natureco models fallbacks set <model-id>'));
|
|
227
|
+
console.log(chalk.gray(' Canlı sınama : ') + chalk.cyan('natureco models list --probe\n'));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function setModel(modelId) {
|
|
231
|
+
const config = getConfig();
|
|
232
|
+
|
|
233
|
+
if (!modelId) {
|
|
234
|
+
const knownModels = getKnownModels(config.providerUrl || '');
|
|
235
|
+
if (knownModels.length === 0) {
|
|
236
|
+
console.log(chalk.red('\n ❌ Bu provider için bilinen model yok. Model ID\'sini parametre olarak verin.\n'));
|
|
237
|
+
console.log(chalk.gray(' Kullanım: natureco models set <model-id>\n'));
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
console.log(chalk.cyan('\n Mevcut Modeller\n'));
|
|
241
|
+
knownModels.forEach((m, i) => {
|
|
242
|
+
console.log(chalk.white(` ${i + 1}. ${m.id}`));
|
|
243
|
+
console.log(chalk.gray(` ${m.label}`));
|
|
244
|
+
});
|
|
245
|
+
console.log('');
|
|
246
|
+
console.log(chalk.gray(' Kullanım: natureco models set <model-id>\n'));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
config.providerModel = modelId;
|
|
251
|
+
saveConfig(config);
|
|
252
|
+
console.log(chalk.green(`\n ✓ Model güncellendi: ${modelId}\n`));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function scanModels(opts) {
|
|
256
|
+
const config = getConfig();
|
|
257
|
+
const providerUrl = config.providerUrl || '';
|
|
258
|
+
const providerHost = providerUrl.replace('https://', '').split('/')[0] || '';
|
|
259
|
+
|
|
260
|
+
const spinner = ['\\', '|', '/', '-'];
|
|
261
|
+
let si = 0;
|
|
262
|
+
const spin = setInterval(() => {
|
|
263
|
+
process.stdout.write(`\r ${chalk.gray(spinner[si % spinner.length])} Modeller taranıyor...`);
|
|
264
|
+
si++;
|
|
265
|
+
}, 150);
|
|
266
|
+
|
|
267
|
+
const allModels = [];
|
|
268
|
+
const errors = [];
|
|
269
|
+
|
|
270
|
+
// 1. Provider /v1/models API'sinden canlı tarama
|
|
271
|
+
const endpoint = findModelsEndpoint(providerUrl);
|
|
272
|
+
if (endpoint) {
|
|
273
|
+
try {
|
|
274
|
+
const live = await fetchLiveModels(endpoint, config.providerApiKey, { timeout: opts.timeout || 10000 });
|
|
275
|
+
allModels.push(...live.map(m => ({ ...m, source: 'live' })));
|
|
276
|
+
} catch (err) {
|
|
277
|
+
errors.push(`API: ${err.message}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 2. OpenRouter ücretsiz model kataloğu
|
|
282
|
+
if (!providerUrl || providerUrl.includes('openrouter')) {
|
|
283
|
+
try {
|
|
284
|
+
const res = await fetch('https://openrouter.ai/api/v1/models', {
|
|
285
|
+
signal: AbortSignal.timeout(opts.timeout || 10000),
|
|
286
|
+
});
|
|
287
|
+
if (res.ok) {
|
|
288
|
+
const data = await res.json();
|
|
289
|
+
const free = (data.data || []).filter(m => m.pricing?.prompt === '0');
|
|
290
|
+
free.forEach(m => {
|
|
291
|
+
if (!allModels.some(ex => ex.id === m.id)) {
|
|
292
|
+
allModels.push({
|
|
293
|
+
id: m.id,
|
|
294
|
+
label: m.name || m.id,
|
|
295
|
+
features: [],
|
|
296
|
+
source: 'openrouter-free',
|
|
297
|
+
context: m.context_length || null,
|
|
298
|
+
pricing: m.pricing,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
} catch (err) {
|
|
304
|
+
errors.push(`OpenRouter: ${err.message}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 3. Bilinen statik modeller
|
|
309
|
+
const known = getKnownModels(providerUrl);
|
|
310
|
+
known.forEach(m => {
|
|
311
|
+
if (!allModels.some(ex => ex.id === m.id)) {
|
|
312
|
+
allModels.push({ ...m, source: 'static' });
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
clearInterval(spin);
|
|
317
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
318
|
+
|
|
319
|
+
if (opts.json) {
|
|
320
|
+
console.log(JSON.stringify({
|
|
321
|
+
models: allModels,
|
|
322
|
+
total: allModels.length,
|
|
323
|
+
sources: {
|
|
324
|
+
live: allModels.filter(m => m.source === 'live').length,
|
|
325
|
+
openrouter_free: allModels.filter(m => m.source === 'openrouter-free').length,
|
|
326
|
+
static: allModels.filter(m => m.source === 'static').length,
|
|
327
|
+
},
|
|
328
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
329
|
+
}, null, 2));
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log('');
|
|
334
|
+
console.log(chalk.gray(' ' + '─'.repeat(48)));
|
|
335
|
+
|
|
336
|
+
if (errors.length > 0) {
|
|
337
|
+
console.log(chalk.yellow(`\n ⚠ ${errors.length} uyarı:`));
|
|
338
|
+
errors.forEach(e => console.log(chalk.gray(` ${e}`)));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const liveCount = allModels.filter(m => m.source === 'live').length;
|
|
342
|
+
const freeCount = allModels.filter(m => m.source === 'openrouter-free').length;
|
|
343
|
+
const staticCount = allModels.filter(m => m.source === 'static').length;
|
|
344
|
+
|
|
345
|
+
console.log(chalk.cyan.bold(`\n Modeller (${allModels.length} bulundu)\n`));
|
|
346
|
+
console.log(chalk.gray(` Kaynak: ${liveCount} canlı, ${freeCount} ücretsiz, ${staticCount} statik\n`));
|
|
347
|
+
|
|
348
|
+
if (liveCount > 0) {
|
|
349
|
+
console.log(chalk.cyan(' Canlı Modeller\n'));
|
|
350
|
+
allModels.filter(m => m.source === 'live').slice(0, 20).forEach(m => {
|
|
351
|
+
console.log(chalk.white(` ${m.id}`));
|
|
352
|
+
if (m.context) console.log(chalk.gray(` Bağlam: ${m.context} token`));
|
|
353
|
+
});
|
|
354
|
+
if (allModels.filter(m => m.source === 'live').length > 20) {
|
|
355
|
+
console.log(chalk.gray(` ... ve ${allModels.filter(m => m.source === 'live').length - 20} model daha`));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (freeCount > 0) {
|
|
360
|
+
console.log(chalk.cyan('\n OpenRouter Ücretsiz Modeller\n'));
|
|
361
|
+
allModels.filter(m => m.source === 'openrouter-free').slice(0, 15).forEach(m => {
|
|
362
|
+
console.log(chalk.white(` ${m.id}`));
|
|
363
|
+
if (m.label && m.label !== m.id) console.log(chalk.gray(` ${m.label}`));
|
|
364
|
+
});
|
|
365
|
+
if (allModels.filter(m => m.source === 'openrouter-free').length > 15) {
|
|
366
|
+
console.log(chalk.gray(` ... ve ${allModels.filter(m => m.source === 'openrouter-free').length - 15} model daha`));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (staticCount > 0 && liveCount === 0) {
|
|
371
|
+
console.log(chalk.cyan('\n Statik Modeller\n'));
|
|
372
|
+
allModels.filter(m => m.source === 'static').forEach(m => {
|
|
373
|
+
const featureIcons = (m.features || []).map(f => FEATURE_ICONS[f] || '').join(' ');
|
|
374
|
+
console.log(chalk.white(` ${m.id}`) + (featureIcons ? ` ${featureIcons}` : ''));
|
|
375
|
+
console.log(chalk.gray(` ${m.label}`));
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const currentModel = config.providerModel || '';
|
|
380
|
+
if (currentModel && !allModels.some(m => m.id === currentModel)) {
|
|
381
|
+
console.log(chalk.yellow(`\n ⚠ Mevcut model "${currentModel}" bu provider için bulunamadı.`));
|
|
382
|
+
console.log(chalk.gray(' Güncellemek için: ') + chalk.cyan('natureco models set <model-id>'));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log('');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function manageAliases(params) {
|
|
389
|
+
const config = getConfig();
|
|
390
|
+
const aliases = config.modelAliases || {};
|
|
391
|
+
|
|
392
|
+
if (params.length === 0) {
|
|
393
|
+
console.log(chalk.cyan.bold('\n Model Takma Adları\n'));
|
|
394
|
+
if (Object.keys(aliases).length === 0) {
|
|
395
|
+
console.log(chalk.gray(' Takma ad yok.\n'));
|
|
396
|
+
console.log(chalk.gray(' Eklemek için: ') + chalk.cyan('natureco models aliases <takma-ad> <model-id>\n'));
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
Object.entries(aliases).forEach(([alias, model]) => {
|
|
400
|
+
console.log(chalk.white(` ${alias.padEnd(15)} → `) + chalk.cyan(model));
|
|
401
|
+
});
|
|
402
|
+
console.log('');
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (params.length === 2) {
|
|
407
|
+
const [alias, modelId] = params;
|
|
408
|
+
aliases[alias] = modelId;
|
|
409
|
+
config.modelAliases = aliases;
|
|
410
|
+
saveConfig(config);
|
|
411
|
+
console.log(chalk.green(`\n ✓ Takma ad eklendi: ${alias} → ${modelId}\n`));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (params.length === 1 && (params[0] === 'clear' || params[0] === '--clear')) {
|
|
416
|
+
config.modelAliases = {};
|
|
417
|
+
saveConfig(config);
|
|
418
|
+
console.log(chalk.green('\n ✓ Tüm takma adlar temizlendi.\n'));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (params.length === 1 && aliases[params[0]]) {
|
|
423
|
+
delete aliases[params[0]];
|
|
424
|
+
config.modelAliases = aliases;
|
|
425
|
+
saveConfig(config);
|
|
426
|
+
console.log(chalk.green(`\n ✓ Takma ad silindi: ${params[0]}\n`));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (params.length === 1) {
|
|
431
|
+
const resolved = resolveModel(params[0]);
|
|
432
|
+
if (resolved) {
|
|
433
|
+
console.log(chalk.cyan(`\n ${params[0]} → ${resolved}\n`));
|
|
434
|
+
} else {
|
|
435
|
+
console.log(chalk.yellow(`\n "${params[0]}" çözülemedi.\n`));
|
|
436
|
+
}
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
console.log(chalk.gray('\n Kullanım: natureco models aliases [<takma-ad> <model-id>]\n'));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function manageFallbacks(params) {
|
|
444
|
+
const config = getConfig();
|
|
445
|
+
|
|
446
|
+
if (params.length === 0 || params[0] === 'list') {
|
|
447
|
+
console.log(chalk.cyan.bold('\n Model Yedekleri\n'));
|
|
448
|
+
console.log(chalk.gray(' Birincil : ') + chalk.white(config.providerModel || 'ayarlanmamış'));
|
|
449
|
+
console.log(chalk.gray(' Yedek : ') + chalk.white(config.fallbackModel || 'ayarlanmamış'));
|
|
450
|
+
console.log('');
|
|
451
|
+
console.log(chalk.gray(' Ayarlamak için: ') + chalk.cyan('natureco models fallbacks set <model-id>\n'));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (params[0] === 'set' && params[1]) {
|
|
456
|
+
config.fallbackModel = params[1];
|
|
457
|
+
saveConfig(config);
|
|
458
|
+
console.log(chalk.green(`\n ✓ Yedek model ayarlandı: ${params[1]}\n`));
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (params[0] === 'clear') {
|
|
463
|
+
delete config.fallbackModel;
|
|
464
|
+
saveConfig(config);
|
|
465
|
+
console.log(chalk.green('\n ✓ Yedek model temizlendi.\n'));
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
console.log(chalk.gray('\n Kullanım: natureco models fallbacks [list|set <model-id>|clear]\n'));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function resolveModel(input) {
|
|
473
|
+
const config = getConfig();
|
|
474
|
+
const aliases = config.modelAliases || {};
|
|
475
|
+
if (aliases[input]) return aliases[input];
|
|
476
|
+
|
|
477
|
+
const knownModels = getKnownModels(config.providerUrl || '');
|
|
478
|
+
const match = knownModels.find(m => m.id === input);
|
|
479
|
+
if (match) return match.id;
|
|
480
|
+
|
|
481
|
+
if (input.includes('/')) return input;
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function getKnownModels(providerUrl) {
|
|
486
|
+
for (const [domain, list] of Object.entries(PROVIDER_MODELS)) {
|
|
487
|
+
if (providerUrl.includes(domain)) {
|
|
488
|
+
return list;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (providerUrl.includes('openai') || providerUrl.includes('v1')) {
|
|
492
|
+
return PROVIDER_MODELS['api.openai.com'];
|
|
493
|
+
}
|
|
494
|
+
return [];
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function findModelsEndpoint(providerUrl) {
|
|
498
|
+
if (!providerUrl) return null;
|
|
499
|
+
for (const pattern of PROVIDER_API_PATTERNS) {
|
|
500
|
+
if (providerUrl.includes(pattern.match)) {
|
|
501
|
+
return pattern.modelsEndpoint || (providerUrl.replace(/\/v1\/.*$|\/$/, '') + '/v1/models');
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (providerUrl.includes('/v1')) {
|
|
505
|
+
return providerUrl.replace(/\/v1\/.*$/, '/v1/models');
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async function fetchLiveModels(endpoint, apiKey, opts) {
|
|
511
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
512
|
+
if (apiKey) {
|
|
513
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const res = await fetch(endpoint, {
|
|
517
|
+
headers,
|
|
518
|
+
signal: AbortSignal.timeout(opts.timeout || 10000),
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
if (!res.ok) {
|
|
522
|
+
if (res.status === 401 || res.status === 403) {
|
|
523
|
+
throw new Error('API key geçersiz veya yetkisiz erişim');
|
|
524
|
+
}
|
|
525
|
+
if (res.status === 404) {
|
|
526
|
+
throw new Error('Provider bu endpoint\'i desteklemiyor (/v1/models)');
|
|
527
|
+
}
|
|
528
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const data = await res.json();
|
|
532
|
+
const rawModels = data.data || data.models || [];
|
|
533
|
+
|
|
534
|
+
return rawModels.map(m => ({
|
|
535
|
+
id: m.id || m.name || String(m),
|
|
536
|
+
label: m.name || m.id || String(m),
|
|
537
|
+
context: m.context_length || m.max_tokens || m.context_window || null,
|
|
538
|
+
features: [],
|
|
539
|
+
}));
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async function probeProvider(providerUrl, apiKey, modelId, opts) {
|
|
543
|
+
const testModel = modelId || 'gpt-4o-mini';
|
|
544
|
+
const testUrl = providerUrl.includes('/v1/')
|
|
545
|
+
? providerUrl
|
|
546
|
+
: providerUrl.replace(/\/$/, '') + '/v1/chat/completions';
|
|
547
|
+
|
|
548
|
+
const start = Date.now();
|
|
549
|
+
try {
|
|
550
|
+
const res = await fetch(testUrl, {
|
|
551
|
+
method: 'POST',
|
|
552
|
+
headers: {
|
|
553
|
+
'Content-Type': 'application/json',
|
|
554
|
+
...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}),
|
|
555
|
+
},
|
|
556
|
+
body: JSON.stringify({
|
|
557
|
+
model: testModel,
|
|
558
|
+
messages: [{ role: 'user', content: 'Respond with only the word "ok".' }],
|
|
559
|
+
max_tokens: 5,
|
|
560
|
+
}),
|
|
561
|
+
signal: AbortSignal.timeout(opts.timeout || 10000),
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
const ms = Date.now() - start;
|
|
565
|
+
if (res.ok) {
|
|
566
|
+
console.log(chalk.green(` ✓ ${testModel}: ${ms}ms (yanıt alındı)`));
|
|
567
|
+
} else if (res.status === 401 || res.status === 403) {
|
|
568
|
+
console.log(chalk.red(` ✗ ${testModel}: Yetkisiz (${res.status})`));
|
|
569
|
+
} else if (res.status === 429) {
|
|
570
|
+
console.log(chalk.yellow(` ⚠ ${testModel}: Rate limit (${res.status})`));
|
|
571
|
+
} else {
|
|
572
|
+
console.log(chalk.yellow(` ⚠ ${testModel}: HTTP ${res.status} (${ms}ms)`));
|
|
573
|
+
}
|
|
574
|
+
} catch (err) {
|
|
575
|
+
const ms = Date.now() - start;
|
|
576
|
+
if (err.name === 'TimeoutError' || err.name === 'AbortError') {
|
|
577
|
+
console.log(chalk.red(` ✗ ${testModel}: Zaman aşımı (${opts.timeout}ms)`));
|
|
578
|
+
} else {
|
|
579
|
+
console.log(chalk.red(` ✗ ${testModel}: ${err.message}`));
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
module.exports = models;
|