natureco-cli 2.23.30 → 2.23.32
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 +178 -167
- package/package.json +1 -1
- package/src/commands/acp.js +39 -0
- package/src/commands/admin-rpc.js +83 -0
- package/src/commands/agent.js +214 -23
- package/src/commands/agents.js +114 -30
- package/src/commands/approvals.js +172 -11
- package/src/commands/ask.js +1 -1
- package/src/commands/browser.js +815 -0
- package/src/commands/capability.js +195 -22
- package/src/commands/channels.js +422 -267
- package/src/commands/chat.js +5 -8
- package/src/commands/clawbot.js +19 -0
- package/src/commands/code.js +3 -2
- package/src/commands/commitments.js +125 -9
- package/src/commands/completion.js +40 -32
- package/src/commands/config.js +228 -30
- package/src/commands/configure.js +84 -67
- package/src/commands/cron.js +239 -19
- package/src/commands/daemon.js +34 -4
- package/src/commands/dashboard.js +47 -374
- package/src/commands/devices.js +53 -26
- package/src/commands/directory.js +146 -14
- package/src/commands/dns.js +148 -10
- package/src/commands/docs.js +119 -26
- package/src/commands/doctor.js +143 -492
- package/src/commands/exec-policy.js +57 -48
- package/src/commands/gateway.js +492 -249
- package/src/commands/health.js +141 -11
- package/src/commands/help.js +24 -25
- package/src/commands/hooks.js +141 -87
- package/src/commands/infer.js +1442 -41
- package/src/commands/logs.js +122 -99
- package/src/commands/mcp.js +121 -309
- package/src/commands/memory.js +128 -0
- package/src/commands/message.js +720 -140
- package/src/commands/models.js +39 -1
- package/src/commands/node.js +77 -77
- package/src/commands/nodes.js +278 -22
- package/src/commands/onboard.js +115 -56
- package/src/commands/pairing.js +108 -107
- package/src/commands/path.js +206 -0
- package/src/commands/plugins.js +35 -1
- package/src/commands/proxy.js +159 -8
- package/src/commands/qr.js +55 -13
- package/src/commands/reset.js +101 -94
- package/src/commands/secrets.js +104 -21
- package/src/commands/sessions.js +110 -51
- package/src/commands/setup.js +229 -649
- package/src/commands/skills.js +67 -1
- package/src/commands/status.js +101 -127
- package/src/commands/tasks.js +208 -100
- package/src/commands/terminal.js +130 -12
- package/src/commands/transcripts.js +24 -1
- package/src/commands/tui.js +41 -0
- package/src/commands/uninstall.js +73 -92
- package/src/commands/update.js +146 -91
- package/src/commands/web-fetch.js +34 -0
- package/src/commands/webhooks.js +58 -66
- package/src/commands/wiki.js +783 -0
- package/src/utils/agents-md.js +85 -0
- package/src/utils/api.js +40 -41
- package/src/utils/format.js +144 -0
- package/src/utils/headless.js +2 -1
- package/src/utils/parallel-tools.js +106 -0
- package/src/utils/sub-agent.js +148 -0
- package/src/utils/token-budget.js +304 -0
- package/src/utils/tool-runner.js +7 -5
- package/src/utils/web-fetch.js +107 -0
package/src/commands/infer.js
CHANGED
|
@@ -1,73 +1,1474 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const { getConfig } = require('../utils/config');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
const [action, ...params] = args || [];
|
|
7
|
+
const STATE_FILE = path.join(os.homedir(), '.natureco', 'infer-state.json');
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
function readState() {
|
|
10
|
+
try {
|
|
11
|
+
if (fs.existsSync(STATE_FILE)) {
|
|
12
|
+
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
} catch {}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function writeState(state) {
|
|
19
|
+
try {
|
|
20
|
+
const dir = path.dirname(STATE_FILE);
|
|
21
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
22
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf8');
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.log(chalk.yellow(' \u26A0 Could not write state: ' + err.message));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const MODEL_FEATURE_MAP = [
|
|
29
|
+
{ pattern: 'vision', features: ['vision', 'multimodal'] },
|
|
30
|
+
{ pattern: 'gpt-4o', features: ['chat', 'vision', 'code'] },
|
|
31
|
+
{ pattern: 'gpt-4-turbo', features: ['chat', 'vision'] },
|
|
32
|
+
{ pattern: 'claude', features: ['chat', 'vision', 'code'] },
|
|
33
|
+
{ pattern: 'llama-3.2', features: ['chat', 'vision'] },
|
|
34
|
+
{ pattern: 'llama-3.3', features: ['chat', 'vision'] },
|
|
35
|
+
{ pattern: 'grok-vision', features: ['chat', 'vision'] },
|
|
36
|
+
{ pattern: 'deepseek-coder', features: ['chat', 'code'] },
|
|
37
|
+
{ pattern: 'codestral', features: ['chat', 'code'] },
|
|
38
|
+
{ pattern: 'qwen-coder', features: ['chat', 'code'] },
|
|
39
|
+
{ pattern: 'embedding', features: ['embedding'] },
|
|
40
|
+
{ pattern: 'tts', features: ['tts'] },
|
|
41
|
+
{ pattern: 'whisper', features: ['stt', 'audio'] },
|
|
42
|
+
{ pattern: 'distil-whisper', features: ['stt', 'audio'] },
|
|
43
|
+
{ pattern: 'dall-e', features: ['images'] },
|
|
44
|
+
{ pattern: 'stable-diffusion', features: ['images'] },
|
|
45
|
+
{ pattern: 'suno', features: ['music'] },
|
|
46
|
+
{ pattern: 'reasoner', features: ['reasoning'] },
|
|
47
|
+
{ pattern: 'o1-preview', features: ['reasoning', 'chat'] },
|
|
48
|
+
{ pattern: 'o1-mini', features: ['reasoning', 'chat'] },
|
|
49
|
+
{ pattern: 'o3', features: ['reasoning', 'chat'] },
|
|
50
|
+
{ pattern: 'sonar', features: ['search', 'chat'] },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
async function infer(args) {
|
|
54
|
+
const [action, sub1, sub2, ...rest] = args || [];
|
|
55
|
+
|
|
56
|
+
if (!action) {
|
|
57
|
+
console.log(chalk.cyan('\n OpenClaw Infer\n'));
|
|
58
|
+
console.log(chalk.gray(' Usage:'));
|
|
59
|
+
console.log(chalk.white(' natureco infer <command> [options]'));
|
|
60
|
+
console.log();
|
|
61
|
+
console.log(chalk.gray(' Commands:'));
|
|
62
|
+
console.log(chalk.white(' models ') + chalk.gray('List available models'));
|
|
63
|
+
console.log(chalk.white(' media ') + chalk.gray('Show media capabilities'));
|
|
64
|
+
console.log(chalk.white(' capabilities ') + chalk.gray('Show provider capabilities'));
|
|
65
|
+
console.log(chalk.white(' list ') + chalk.gray('List model providers'));
|
|
66
|
+
console.log(chalk.white(' inspect <provider> ') + chalk.gray('Inspect a provider'));
|
|
67
|
+
console.log(chalk.white(' model run <prompt> ') + chalk.gray('Run inference'));
|
|
68
|
+
console.log(chalk.white(' model list ') + chalk.gray('List models'));
|
|
69
|
+
console.log(chalk.white(' model inspect <model> ') + chalk.gray('Inspect a model'));
|
|
70
|
+
console.log(chalk.white(' model providers ') + chalk.gray('List model providers'));
|
|
71
|
+
console.log(chalk.white(' model auth login <provider> ') + chalk.gray('Authenticate with provider'));
|
|
72
|
+
console.log(chalk.white(' model auth logout <provider> ') + chalk.gray('Logout from provider'));
|
|
73
|
+
console.log(chalk.white(' model auth status ') + chalk.gray('Show auth status'));
|
|
74
|
+
console.log(chalk.white(' image generate <prompt> ') + chalk.gray('Generate image'));
|
|
75
|
+
console.log(chalk.white(' image edit <path> <prompt> ') + chalk.gray('Edit an image'));
|
|
76
|
+
console.log(chalk.white(' image describe <path> ') + chalk.gray('Describe an image'));
|
|
77
|
+
console.log(chalk.white(' image describe-many <dir> ') + chalk.gray('Describe multiple images'));
|
|
78
|
+
console.log(chalk.white(' image providers ') + chalk.gray('List image providers'));
|
|
79
|
+
console.log(chalk.white(' audio transcribe <path> ') + chalk.gray('Transcribe audio'));
|
|
80
|
+
console.log(chalk.white(' audio providers ') + chalk.gray('List audio providers'));
|
|
81
|
+
console.log(chalk.white(' tts convert <text> ') + chalk.gray('Text to speech'));
|
|
82
|
+
console.log(chalk.white(' tts voices ') + chalk.gray('List voices'));
|
|
83
|
+
console.log(chalk.white(' tts providers ') + chalk.gray('List TTS providers'));
|
|
84
|
+
console.log(chalk.white(' tts status ') + chalk.gray('Show TTS status'));
|
|
85
|
+
console.log(chalk.white(' tts enable ') + chalk.gray('Enable TTS'));
|
|
86
|
+
console.log(chalk.white(' tts disable ') + chalk.gray('Disable TTS'));
|
|
87
|
+
console.log(chalk.white(' tts set-provider <name> ') + chalk.gray('Set TTS provider'));
|
|
88
|
+
console.log(chalk.white(' video generate <prompt> ') + chalk.gray('Generate video'));
|
|
89
|
+
console.log(chalk.white(' video describe <path> ') + chalk.gray('Describe video'));
|
|
90
|
+
console.log(chalk.white(' video providers ') + chalk.gray('List video providers'));
|
|
91
|
+
console.log(chalk.white(' web search <query> ') + chalk.gray('Web search'));
|
|
92
|
+
console.log(chalk.white(' web fetch <url> ') + chalk.gray('Fetch a URL'));
|
|
93
|
+
console.log(chalk.white(' web providers ') + chalk.gray('List web providers'));
|
|
94
|
+
console.log(chalk.white(' embedding create <text> ') + chalk.gray('Create embedding'));
|
|
95
|
+
console.log(chalk.white(' embedding providers ') + chalk.gray('List embedding providers'));
|
|
96
|
+
console.log(chalk.white(' auth <provider> ') + chalk.gray('Test authentication'));
|
|
97
|
+
console.log(chalk.white(' auth add <provider> <key> ') + chalk.gray('Add auth key'));
|
|
98
|
+
console.log(chalk.white(' auth login <provider> ') + chalk.gray('OAuth login'));
|
|
99
|
+
console.log(chalk.white(' auth login-github-copilot ') + chalk.gray('GitHub Copilot auth'));
|
|
100
|
+
console.log(chalk.white(' auth setup-token ') + chalk.gray('Setup token auth'));
|
|
101
|
+
console.log(chalk.white(' auth paste-token ') + chalk.gray('Paste auth token'));
|
|
102
|
+
console.log(chalk.white(' auth order get ') + chalk.gray('Get auth order'));
|
|
103
|
+
console.log(chalk.white(' auth order set <providers..> ') + chalk.gray('Set auth order'));
|
|
104
|
+
console.log(chalk.white(' auth order clear ') + chalk.gray('Clear auth order'));
|
|
105
|
+
console.log();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (action === 'models') return inferModels();
|
|
8
110
|
if (action === 'media') return inferMedia();
|
|
9
111
|
if (action === 'capabilities') return inferCapabilities();
|
|
112
|
+
if (action === 'list') return inferList();
|
|
113
|
+
|
|
114
|
+
if (action === 'inspect') {
|
|
115
|
+
if (!sub1) {
|
|
116
|
+
console.log(chalk.red('\n \u274C Provider name required\n'));
|
|
117
|
+
console.log(chalk.gray(' Usage: natureco infer inspect <provider>\n'));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
return inferInspect(sub1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (action === 'model') {
|
|
124
|
+
if (!sub1) {
|
|
125
|
+
console.log(chalk.red('\n \u274C Model subcommand required\n'));
|
|
126
|
+
console.log(chalk.gray(' Usage: natureco infer model <run|list|inspect|providers|auth>\n'));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
if (sub1 === 'run') {
|
|
130
|
+
if (!sub2) {
|
|
131
|
+
console.log(chalk.red('\n \u274C Prompt required\n'));
|
|
132
|
+
console.log(chalk.gray(' Usage: natureco infer model run [<model>] <prompt>\n'));
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
if (rest.length === 0) {
|
|
136
|
+
return inferModelRun(sub2, null);
|
|
137
|
+
}
|
|
138
|
+
return inferModelRun(rest.join(' '), sub2);
|
|
139
|
+
}
|
|
140
|
+
if (sub1 === 'list') return inferModelList();
|
|
141
|
+
if (sub1 === 'inspect') {
|
|
142
|
+
if (!sub2) {
|
|
143
|
+
console.log(chalk.red('\n \u274C Model name required\n'));
|
|
144
|
+
console.log(chalk.gray(' Usage: natureco infer model inspect <model>\n'));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
return inferModelInspect(sub2);
|
|
148
|
+
}
|
|
149
|
+
if (sub1 === 'providers') return inferModelProviders();
|
|
150
|
+
if (sub1 === 'auth') {
|
|
151
|
+
if (sub2 === 'login') {
|
|
152
|
+
if (!rest[0]) {
|
|
153
|
+
console.log(chalk.red('\n \u274C Provider name required\n'));
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
return inferModelAuthLogin(rest[0]);
|
|
157
|
+
}
|
|
158
|
+
if (sub2 === 'logout') {
|
|
159
|
+
if (!rest[0]) {
|
|
160
|
+
console.log(chalk.red('\n \u274C Provider name required\n'));
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
return inferModelAuthLogout(rest[0]);
|
|
164
|
+
}
|
|
165
|
+
if (sub2 === 'status') return inferModelAuthStatus();
|
|
166
|
+
console.log(chalk.red('\n \u274C Unknown model auth subcommand\n'));
|
|
167
|
+
console.log(chalk.gray(' Usage: natureco infer model auth <login|logout|status>\n'));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
console.log(chalk.red('\n \u274C Unknown model subcommand: ' + sub1 + '\n'));
|
|
171
|
+
console.log(chalk.gray(' Usage: natureco infer model <run|list|inspect|providers|auth>\n'));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (action === 'image') {
|
|
176
|
+
if (!sub1) {
|
|
177
|
+
return inferImage('');
|
|
178
|
+
}
|
|
179
|
+
if (sub1 === 'generate') {
|
|
180
|
+
const prompt = [sub2, ...rest].join(' ');
|
|
181
|
+
if (!prompt.trim()) {
|
|
182
|
+
console.log(chalk.red('\n \u274C Prompt required\n'));
|
|
183
|
+
console.log(chalk.gray(' Usage: natureco infer image generate <prompt>\n'));
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
return inferImageGenerate(prompt);
|
|
187
|
+
}
|
|
188
|
+
if (sub1 === 'edit') {
|
|
189
|
+
if (!sub2) {
|
|
190
|
+
console.log(chalk.red('\n \u274C Image path required\n'));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
const prompt = rest.join(' ');
|
|
194
|
+
if (!prompt.trim()) {
|
|
195
|
+
console.log(chalk.red('\n \u274C Edit prompt required\n'));
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
return inferImageEdit(sub2, prompt);
|
|
199
|
+
}
|
|
200
|
+
if (sub1 === 'describe') {
|
|
201
|
+
if (!sub2) {
|
|
202
|
+
console.log(chalk.red('\n \u274C Image path required\n'));
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
return inferImageDescribe(sub2);
|
|
206
|
+
}
|
|
207
|
+
if (sub1 === 'describe-many') {
|
|
208
|
+
if (!sub2) {
|
|
209
|
+
console.log(chalk.red('\n \u274C Directory path required\n'));
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
return inferImageDescribeMany(sub2);
|
|
213
|
+
}
|
|
214
|
+
if (sub1 === 'providers') return inferImageProviders();
|
|
215
|
+
const oldPrompt = [sub1, sub2, ...rest].join(' ');
|
|
216
|
+
return inferImage(oldPrompt);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (action === 'audio') {
|
|
220
|
+
if (sub1 === 'transcribe') {
|
|
221
|
+
if (!sub2) {
|
|
222
|
+
console.log(chalk.red('\n \u274C Audio file path required\n'));
|
|
223
|
+
console.log(chalk.gray(' Usage: natureco infer audio transcribe <path>\n'));
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
return inferAudioTranscribe(sub2);
|
|
227
|
+
}
|
|
228
|
+
if (sub1 === 'providers') return inferAudioProviders();
|
|
229
|
+
const oldPrompt = [sub1, sub2, ...rest].join(' ');
|
|
230
|
+
return inferAudio(oldPrompt);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (action === 'tts') {
|
|
234
|
+
if (sub1 === 'convert') {
|
|
235
|
+
const text = [sub2, ...rest].join(' ');
|
|
236
|
+
if (!text.trim()) {
|
|
237
|
+
console.log(chalk.red('\n \u274C Text required\n'));
|
|
238
|
+
console.log(chalk.gray(' Usage: natureco infer tts convert <text>\n'));
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
return inferTtsConvert(text);
|
|
242
|
+
}
|
|
243
|
+
if (sub1 === 'voices') return inferTtsVoices();
|
|
244
|
+
if (sub1 === 'providers') return inferTtsProviders();
|
|
245
|
+
if (sub1 === 'status') return inferTtsStatus();
|
|
246
|
+
if (sub1 === 'enable') return inferTtsEnable();
|
|
247
|
+
if (sub1 === 'disable') return inferTtsDisable();
|
|
248
|
+
if (sub1 === 'set-provider') {
|
|
249
|
+
if (!sub2) {
|
|
250
|
+
console.log(chalk.red('\n \u274C Provider name required\n'));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
return inferTtsSetProvider(sub2);
|
|
254
|
+
}
|
|
255
|
+
const oldText = [sub1, sub2, ...rest].join(' ');
|
|
256
|
+
return inferTts(oldText);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (action === 'video') {
|
|
260
|
+
if (sub1 === 'generate') {
|
|
261
|
+
const prompt = [sub2, ...rest].join(' ');
|
|
262
|
+
if (!prompt.trim()) {
|
|
263
|
+
console.log(chalk.red('\n \u274C Prompt required\n'));
|
|
264
|
+
console.log(chalk.gray(' Usage: natureco infer video generate <prompt>\n'));
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
return inferVideoGenerate(prompt);
|
|
268
|
+
}
|
|
269
|
+
if (sub1 === 'describe') {
|
|
270
|
+
if (!sub2) {
|
|
271
|
+
console.log(chalk.red('\n \u274C Video path required\n'));
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
return inferVideoDescribe(sub2);
|
|
275
|
+
}
|
|
276
|
+
if (sub1 === 'providers') return inferVideoProviders();
|
|
277
|
+
const oldPrompt = [sub1, sub2, ...rest].join(' ');
|
|
278
|
+
return inferVideo(oldPrompt);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (action === 'web') {
|
|
282
|
+
if (sub1 === 'search') {
|
|
283
|
+
const query = [sub2, ...rest].join(' ');
|
|
284
|
+
if (!query.trim()) {
|
|
285
|
+
console.log(chalk.red('\n \u274C Search query required\n'));
|
|
286
|
+
console.log(chalk.gray(' Usage: natureco infer web search <query>\n'));
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
return inferWebSearch(query);
|
|
290
|
+
}
|
|
291
|
+
if (sub1 === 'fetch') {
|
|
292
|
+
if (!sub2) {
|
|
293
|
+
console.log(chalk.red('\n \u274C URL required\n'));
|
|
294
|
+
console.log(chalk.gray(' Usage: natureco infer web fetch <url>\n'));
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
return inferWebFetch(sub2);
|
|
298
|
+
}
|
|
299
|
+
if (sub1 === 'providers') return inferWebProviders();
|
|
300
|
+
const oldQuery = [sub1, sub2, ...rest].join(' ');
|
|
301
|
+
return inferWeb(oldQuery);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (action === 'embedding') {
|
|
305
|
+
if (sub1 === 'create') {
|
|
306
|
+
const text = [sub2, ...rest].join(' ');
|
|
307
|
+
if (!text.trim()) {
|
|
308
|
+
console.log(chalk.red('\n \u274C Text required\n'));
|
|
309
|
+
console.log(chalk.gray(' Usage: natureco infer embedding create <text>\n'));
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
return inferEmbeddingCreate(text);
|
|
313
|
+
}
|
|
314
|
+
if (sub1 === 'providers') return inferEmbeddingProviders();
|
|
315
|
+
const oldText = [sub1, sub2, ...rest].join(' ');
|
|
316
|
+
return inferEmbedding(oldText);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (action === 'auth') {
|
|
320
|
+
if (!sub1) {
|
|
321
|
+
return inferAuth();
|
|
322
|
+
}
|
|
323
|
+
if (sub1 === 'add') {
|
|
324
|
+
if (!sub2 || rest.length === 0) {
|
|
325
|
+
console.log(chalk.red('\n \u274C Usage: natureco infer auth add <provider> <key>\n'));
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
return inferAuthAdd(sub2, rest.join(' '));
|
|
329
|
+
}
|
|
330
|
+
if (sub1 === 'login') {
|
|
331
|
+
if (!sub2) {
|
|
332
|
+
console.log(chalk.red('\n \u274C Provider name required\n'));
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
return inferAuthLogin(sub2);
|
|
336
|
+
}
|
|
337
|
+
if (sub1 === 'login-github-copilot') return inferAuthLoginGithubCopilot();
|
|
338
|
+
if (sub1 === 'setup-token') return inferAuthSetupToken();
|
|
339
|
+
if (sub1 === 'paste-token') return inferAuthPasteToken();
|
|
340
|
+
if (sub1 === 'order') {
|
|
341
|
+
if (sub2 === 'get') return inferAuthOrderGet();
|
|
342
|
+
if (sub2 === 'set') {
|
|
343
|
+
if (rest.length === 0) {
|
|
344
|
+
console.log(chalk.red('\n \u274C Usage: natureco infer auth order set <providers...>\n'));
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
return inferAuthOrderSet(rest);
|
|
348
|
+
}
|
|
349
|
+
if (sub2 === 'clear') return inferAuthOrderClear();
|
|
350
|
+
console.log(chalk.red('\n \u274C Unknown order subcommand\n'));
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
return inferAuth(sub1);
|
|
354
|
+
}
|
|
10
355
|
|
|
11
|
-
console.log(chalk.red(
|
|
12
|
-
console.log(chalk.gray('
|
|
356
|
+
console.log(chalk.red('\n \u274C Unknown command: ' + action + '\n'));
|
|
357
|
+
console.log(chalk.gray(' Usage: natureco infer [models|media|capabilities|list|inspect|model|image|audio|tts|video|web|embedding|auth]\n'));
|
|
13
358
|
process.exit(1);
|
|
14
359
|
}
|
|
15
360
|
|
|
16
|
-
|
|
361
|
+
// ---------------------------------------------------------------------------
|
|
362
|
+
// Existing functions (unchanged)
|
|
363
|
+
// ---------------------------------------------------------------------------
|
|
364
|
+
|
|
365
|
+
async function inferModels() {
|
|
17
366
|
const config = getConfig();
|
|
18
|
-
|
|
19
|
-
|
|
367
|
+
const providerUrl = config.providerUrl || '';
|
|
368
|
+
const apiKey = config.providerApiKey || '';
|
|
369
|
+
|
|
370
|
+
console.log(chalk.cyan('\n Inferring models...\n'));
|
|
371
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
372
|
+
|
|
373
|
+
const providers = [
|
|
374
|
+
{ name: 'openai', url: 'https://api.openai.com/v1/models', key: config.openaiApiKey || process.env.OPENAI_API_KEY },
|
|
375
|
+
{ name: 'anthropic', url: null, key: config.anthropicApiKey || process.env.ANTHROPIC_API_KEY },
|
|
376
|
+
{ name: 'groq', url: 'https://api.groq.com/openai/v1/models', key: config.groqApiKey || process.env.GROQ_API_KEY },
|
|
377
|
+
{ name: 'deepseek', url: 'https://api.deepseek.com/v1/models', key: config.deepseekApiKey || process.env.DEEPSEEK_API_KEY },
|
|
378
|
+
{ name: 'mistral', url: 'https://api.mistral.ai/v1/models', key: config.mistralApiKey || process.env.MISTRAL_API_KEY },
|
|
379
|
+
{ name: 'xai', url: 'https://api.x.ai/v1/models', key: config.xaiApiKey || process.env.XAI_API_KEY },
|
|
380
|
+
{ name: 'perplexity', url: 'https://api.perplexity.ai/v1/models', key: config.perplexityApiKey || process.env.PERPLEXITY_API_KEY },
|
|
381
|
+
{ name: 'together', url: 'https://api.together.xyz/v1/models', key: config.togetherApiKey || process.env.TOGETHER_API_KEY },
|
|
382
|
+
{ name: 'openrouter', url: 'https://openrouter.ai/api/v1/models', key: config.openrouterApiKey || process.env.OPENROUTER_API_KEY },
|
|
383
|
+
];
|
|
20
384
|
|
|
21
|
-
const providers = ['openai', 'anthropic', 'groq', 'deepseek', 'xai'];
|
|
22
385
|
for (const p of providers) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
386
|
+
if (!p.key) {
|
|
387
|
+
console.log(' ' + chalk.gray('\u25CB') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.gray('not configured'));
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!p.url) {
|
|
392
|
+
console.log(' ' + chalk.green('\u25CF') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.gray('API key found'));
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
const res = await fetch(p.url, {
|
|
398
|
+
headers: { Authorization: 'Bearer ' + p.key, 'Content-Type': 'application/json' },
|
|
399
|
+
signal: AbortSignal.timeout(5000),
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
if (res.ok) {
|
|
403
|
+
const data = await res.json();
|
|
404
|
+
const models = (data.data || data.models || []).map(m => m.id || m);
|
|
405
|
+
const modelCount = models.length;
|
|
406
|
+
const supported = models.slice(0, 5).join(', ');
|
|
407
|
+
const more = modelCount > 5 ? '... +' + (modelCount - 5) : '';
|
|
408
|
+
console.log(' ' + chalk.green('\u25CF') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.gray(modelCount + ' models'));
|
|
409
|
+
console.log(' ' + chalk.gray(supported) + ' ' + more);
|
|
410
|
+
} else if (res.status === 401) {
|
|
411
|
+
console.log(' ' + chalk.red('\u2717') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.red('invalid API key'));
|
|
412
|
+
} else if (res.status === 404) {
|
|
413
|
+
console.log(' ' + chalk.green('\u25CF') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.gray('API key found (no model list endpoint)'));
|
|
414
|
+
} else {
|
|
415
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.gray('HTTP ' + res.status));
|
|
416
|
+
}
|
|
417
|
+
} catch (err) {
|
|
418
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' ' + chalk.white(p.name.padEnd(12)) + ': ' + chalk.gray(err.message));
|
|
419
|
+
}
|
|
26
420
|
}
|
|
27
421
|
|
|
28
|
-
console.log(chalk.gray('\n
|
|
422
|
+
console.log(chalk.gray('\n Set model: ') + chalk.cyan('natureco models set <model-id>\n'));
|
|
29
423
|
}
|
|
30
424
|
|
|
31
|
-
function inferMedia() {
|
|
425
|
+
async function inferMedia() {
|
|
32
426
|
const config = getConfig();
|
|
33
|
-
|
|
34
|
-
console.log(chalk.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{
|
|
427
|
+
|
|
428
|
+
console.log(chalk.cyan('\n Inferring media capabilities...\n'));
|
|
429
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
430
|
+
|
|
431
|
+
const providerUrl = config.providerUrl || '';
|
|
432
|
+
const apiKey = config.providerApiKey || '';
|
|
433
|
+
|
|
434
|
+
let liveModels = [];
|
|
435
|
+
if (apiKey && providerUrl) {
|
|
436
|
+
try {
|
|
437
|
+
const endpoint = providerUrl.replace(/\/v1\/.*$|\/$/, '') + '/v1/models';
|
|
438
|
+
const res = await fetch(endpoint, {
|
|
439
|
+
headers: { Authorization: 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
|
|
440
|
+
signal: AbortSignal.timeout(5000),
|
|
441
|
+
});
|
|
442
|
+
if (res.ok) {
|
|
443
|
+
const data = await res.json();
|
|
444
|
+
liveModels = (data.data || data.models || []).map(m => ({ id: m.id || m }));
|
|
445
|
+
}
|
|
446
|
+
} catch {}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const mediaChecks = [
|
|
450
|
+
{ name: 'Image Generation', models: [], checkKey: ['openaiApiKey', 'falApiKey', 'togetherApiKey'] },
|
|
451
|
+
{ name: 'Video Generation', models: [], checkKey: ['runwayApiKey'] },
|
|
452
|
+
{ name: 'Music Generation', models: [], checkKey: ['sunoApiKey', 'udioApiKey', 'elevenlabsApiKey'] },
|
|
453
|
+
{ name: 'Speech-to-Text', models: [], checkKey: ['openaiApiKey'] },
|
|
454
|
+
{ name: 'Text-to-Speech', models: [], checkKey: ['elevenlabsApiKey', 'openaiApiKey'] },
|
|
455
|
+
{ name: 'Audio Understanding', models: [], checkKey: ['openaiApiKey'] },
|
|
456
|
+
{ name: 'Image Analysis (Vision)', models: [], checkKey: ['openaiApiKey', 'anthropicApiKey'] },
|
|
43
457
|
];
|
|
44
458
|
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
459
|
+
if (liveModels.length > 0) {
|
|
460
|
+
for (const check of mediaChecks) {
|
|
461
|
+
if (check.name === 'Speech-to-Text') {
|
|
462
|
+
check.models = liveModels.filter(m => m.id.toLowerCase().includes('whisper') || m.id.toLowerCase().includes('distil-whisper'));
|
|
463
|
+
} else if (check.name === 'Text-to-Speech') {
|
|
464
|
+
check.models = liveModels.filter(m => m.id.toLowerCase().includes('tts'));
|
|
465
|
+
} else if (check.name === 'Image Analysis (Vision)') {
|
|
466
|
+
check.models = liveModels.filter(m => m.id.toLowerCase().includes('vision') || m.id.toLowerCase().includes('gpt-4o') || m.id.toLowerCase().includes('claude'));
|
|
467
|
+
} else if (check.name === 'Audio Understanding') {
|
|
468
|
+
check.models = liveModels.filter(m => m.id.toLowerCase().includes('whisper') || m.id.toLowerCase().includes('audio'));
|
|
469
|
+
} else if (check.name === 'Image Generation') {
|
|
470
|
+
check.models = liveModels.filter(m => m.id.toLowerCase().includes('dall-e') || m.id.toLowerCase().includes('stable-diffusion') || m.id.toLowerCase().includes('flux'));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
for (const c of mediaChecks) {
|
|
476
|
+
const hasKey = c.checkKey.some(k => config[k] || process.env[k.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase()]);
|
|
477
|
+
const hasModel = c.models.length > 0;
|
|
478
|
+
const available = hasKey || hasModel;
|
|
479
|
+
const icon = available ? chalk.green('\u25CF') : chalk.gray('\u25CB');
|
|
480
|
+
const detail = hasModel
|
|
481
|
+
? chalk.gray('(' + c.models.slice(0, 2).map(m => m.id).join(', ') + ')')
|
|
482
|
+
: hasKey ? chalk.gray('(API key found)') : '';
|
|
483
|
+
console.log(' ' + icon + ' ' + chalk.white(c.name.padEnd(24)) + ' ' + detail);
|
|
49
484
|
}
|
|
50
485
|
console.log();
|
|
51
486
|
}
|
|
52
487
|
|
|
53
|
-
function inferCapabilities() {
|
|
488
|
+
async function inferCapabilities() {
|
|
54
489
|
const config = getConfig();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
490
|
+
const providerUrl = config.providerUrl || '';
|
|
491
|
+
const apiKey = config.providerApiKey || '';
|
|
492
|
+
|
|
493
|
+
console.log(chalk.cyan('\n Inferring provider capabilities...\n'));
|
|
494
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
495
|
+
|
|
496
|
+
let liveModels = [];
|
|
497
|
+
if (apiKey && providerUrl) {
|
|
498
|
+
console.log(' ' + chalk.gray('Fetching models from provider...'));
|
|
499
|
+
try {
|
|
500
|
+
const endpoint = providerUrl.replace(/\/v1\/.*$|\/$/, '') + '/v1/models';
|
|
501
|
+
const res = await fetch(endpoint, {
|
|
502
|
+
headers: { Authorization: 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
|
|
503
|
+
signal: AbortSignal.timeout(5000),
|
|
504
|
+
});
|
|
505
|
+
if (res.ok) {
|
|
506
|
+
const data = await res.json();
|
|
507
|
+
liveModels = (data.data || data.models || []).map(m => ({ id: m.id || m }));
|
|
508
|
+
console.log(' ' + chalk.green('\u2713') + ' ' + liveModels.length + ' models fetched\n');
|
|
509
|
+
} else if (res.status === 401) {
|
|
510
|
+
console.log(' ' + chalk.red('\u2717') + ' Invalid API key\n');
|
|
511
|
+
} else if (res.status === 404) {
|
|
512
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' Provider does not expose /v1/models\n');
|
|
513
|
+
} else {
|
|
514
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' HTTP ' + res.status + '\n');
|
|
515
|
+
}
|
|
516
|
+
} catch (err) {
|
|
517
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' ' + err.message + '\n');
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (liveModels.length === 0) {
|
|
522
|
+
const knownModels = getKnownModels(providerUrl);
|
|
523
|
+
if (knownModels.length > 0) {
|
|
524
|
+
liveModels = knownModels.map(m => ({ id: m }));
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const caps = {};
|
|
529
|
+
for (const [model] of Object.entries(liveModels)) {
|
|
530
|
+
const m = model || liveModels[model];
|
|
531
|
+
if (typeof m === 'string' || !m.id) {
|
|
532
|
+
const id = typeof m === 'string' ? m : '';
|
|
533
|
+
if (!id) continue;
|
|
534
|
+
for (const entry of MODEL_FEATURE_MAP) {
|
|
535
|
+
if (id.toLowerCase().includes(entry.pattern)) {
|
|
536
|
+
for (const feature of entry.features) {
|
|
537
|
+
caps[feature] = (caps[feature] || 0) + 1;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (id.includes('llama') || id.includes('gpt') || id.includes('claude') || id.includes('mixtral') || id.includes('mistral') || id.includes('deepseek') || id.includes('grok')) {
|
|
542
|
+
caps['chat'] = (caps['chat'] || 0) + 1;
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
const id = m.id || '';
|
|
546
|
+
for (const entry of MODEL_FEATURE_MAP) {
|
|
547
|
+
if (id.toLowerCase().includes(entry.pattern)) {
|
|
548
|
+
for (const feature of entry.features) {
|
|
549
|
+
caps[feature] = (caps[feature] || 0) + 1;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (id.includes('llama') || id.includes('gpt') || id.includes('claude') || id.includes('mixtral') || id.includes('mistral') || id.includes('deepseek') || id.includes('grok')) {
|
|
554
|
+
caps['chat'] = (caps['chat'] || 0) + 1;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (!caps['chat'] && (apiKey || config.providerModel)) {
|
|
560
|
+
caps['chat'] = 1;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const capList = [
|
|
564
|
+
{ name: 'Chat', key: 'chat' },
|
|
565
|
+
{ name: 'Vision', key: 'vision' },
|
|
566
|
+
{ name: 'Code Generation', key: 'code' },
|
|
567
|
+
{ name: 'Reasoning', key: 'reasoning' },
|
|
568
|
+
{ name: 'Embeddings', key: 'embedding' },
|
|
569
|
+
{ name: 'TTS', key: 'tts' },
|
|
570
|
+
{ name: 'STT', key: 'stt' },
|
|
571
|
+
{ name: 'Image Gen', key: 'images' },
|
|
572
|
+
{ name: 'Search', key: 'search' },
|
|
573
|
+
{ name: 'Audio', key: 'audio' },
|
|
574
|
+
{ name: 'Music', key: 'music' },
|
|
64
575
|
];
|
|
65
576
|
|
|
66
|
-
for (const c of
|
|
67
|
-
const
|
|
68
|
-
|
|
577
|
+
for (const c of capList) {
|
|
578
|
+
const count = caps[c.key] || 0;
|
|
579
|
+
const icon = count > 0 ? chalk.green('\u25CF (' + count + ')') : chalk.gray('\u25CB');
|
|
580
|
+
console.log(' ' + icon + ' ' + chalk.white(c.name.padEnd(16)));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (liveModels.length > 0) {
|
|
584
|
+
console.log(chalk.gray('\n Probe chat endpoint...'));
|
|
585
|
+
try {
|
|
586
|
+
const providerHost = providerUrl.replace('https://', '').split('/')[0] || '';
|
|
587
|
+
const testUrl = providerUrl.includes('/v1/')
|
|
588
|
+
? providerUrl
|
|
589
|
+
: providerUrl.replace(/\/$/, '') + '/v1/chat/completions';
|
|
590
|
+
const testModel = caps['reasoning'] > 0
|
|
591
|
+
? liveModels.find(m => (m.id || '').includes('reasoner') || (m.id || '').includes('o1'))?.id || liveModels[0]?.id || config.providerModel || 'gpt-4o-mini'
|
|
592
|
+
: config.providerModel || liveModels[0]?.id || 'gpt-4o-mini';
|
|
593
|
+
|
|
594
|
+
const probeRes = await fetch(testUrl, {
|
|
595
|
+
method: 'POST',
|
|
596
|
+
headers: { Authorization: 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
|
|
597
|
+
body: JSON.stringify({
|
|
598
|
+
model: testModel,
|
|
599
|
+
messages: [{ role: 'user', content: 'Respond with word: ok' }],
|
|
600
|
+
max_tokens: 5,
|
|
601
|
+
}),
|
|
602
|
+
signal: AbortSignal.timeout(8000),
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
if (probeRes.ok) {
|
|
606
|
+
const provider = providerHost || config.provider || 'unknown';
|
|
607
|
+
console.log(' ' + chalk.green('\u2713') + ' ' + chalk.white('Chat API') + ': ' + chalk.green('working'));
|
|
608
|
+
if (caps['vision'] > 0) {
|
|
609
|
+
console.log(' ' + chalk.green('\u25CF') + ' ' + chalk.white('Vision') + ': ' + chalk.gray('model supports vision'));
|
|
610
|
+
}
|
|
611
|
+
} else {
|
|
612
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' ' + chalk.white('Chat API') + ': ' + chalk.gray('HTTP ' + probeRes.status));
|
|
613
|
+
}
|
|
614
|
+
} catch (err) {
|
|
615
|
+
console.log(' ' + chalk.yellow('\u26A0') + ' ' + chalk.white('Chat API') + ': ' + chalk.gray(err.message));
|
|
616
|
+
}
|
|
69
617
|
}
|
|
618
|
+
|
|
70
619
|
console.log();
|
|
71
620
|
}
|
|
72
621
|
|
|
622
|
+
function getKnownModels(providerUrl) {
|
|
623
|
+
const providerModels = {
|
|
624
|
+
'api.groq.com': ['llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'llama-3.2-90b-vision-preview', 'llama-3.1-70b-versatile', 'mixtral-8x7b-32768', 'gemma2-9b-it', 'llama-3.2-1b-preview', 'llama-3.2-3b-preview', 'distil-whisper-large-v3-en'],
|
|
625
|
+
'api.openai.com': ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini', 'dall-e-3', 'tts-1', 'whisper-1', 'text-embedding-3-large'],
|
|
626
|
+
'api.anthropic.com': ['claude-opus-4-7', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'claude-3-5-sonnet-20241022', 'claude-3-haiku-20240307'],
|
|
627
|
+
'api.together.xyz': ['meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo', 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', 'mistralai/Mixtral-8x7B-Instruct-v0.1', 'deepseek-ai/deepseek-coder-33b-instruct'],
|
|
628
|
+
'api.deepseek.com': ['deepseek-chat', 'deepseek-coder', 'deepseek-reasoner'],
|
|
629
|
+
'api.mistral.ai': ['mistral-large-latest', 'mistral-medium-latest', 'mistral-small-latest', 'codestral-latest'],
|
|
630
|
+
'api.perplexity.ai': ['sonar-pro', 'sonar', 'sonar-reasoning-pro'],
|
|
631
|
+
'api.x.ai': ['grok-beta', 'grok-vision-beta'],
|
|
632
|
+
'api.deepinfra.com': ['meta-llama/Meta-Llama-3.1-70B-Instruct', 'meta-llama/Meta-Llama-3.1-8B-Instruct', 'mistralai/Mixtral-8x22B-Instruct-v0.1'],
|
|
633
|
+
};
|
|
634
|
+
for (const [domain, models] of Object.entries(providerModels)) {
|
|
635
|
+
if (providerUrl.includes(domain)) return models;
|
|
636
|
+
}
|
|
637
|
+
if (providerUrl.includes('openai') || providerUrl.includes('v1')) {
|
|
638
|
+
return providerModels['api.openai.com'];
|
|
639
|
+
}
|
|
640
|
+
return [];
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async function inferModelRun(prompt, modelOverride) {
|
|
644
|
+
if (!prompt) {
|
|
645
|
+
console.log(chalk.red('\n \u274C Prompt gerekli\n'));
|
|
646
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer model run [<model>] <prompt>\n'));
|
|
647
|
+
process.exit(1);
|
|
648
|
+
}
|
|
649
|
+
const config = getConfig();
|
|
650
|
+
const providerUrl = config.providerUrl;
|
|
651
|
+
const apiKey = config.providerApiKey || config.apiKey;
|
|
652
|
+
const model = modelOverride || config.providerModel || 'gpt-4o-mini';
|
|
653
|
+
if (!providerUrl || !apiKey) {
|
|
654
|
+
console.log(chalk.red('\n \u274C Provider not configured. Run setup first.\n'));
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
console.log(chalk.cyan('\n Running inference...\n'));
|
|
658
|
+
console.log(chalk.gray(' Model : ') + chalk.white(model));
|
|
659
|
+
console.log(chalk.gray(' Prompt : ') + chalk.white(prompt.slice(0, 80) + (prompt.length > 80 ? '...' : '')));
|
|
660
|
+
console.log('');
|
|
661
|
+
const endpoint = providerUrl.includes('/v1/') ? providerUrl : providerUrl.replace(/\/$/, '') + '/v1/chat/completions';
|
|
662
|
+
try {
|
|
663
|
+
const res = await fetch(endpoint, {
|
|
664
|
+
method: 'POST',
|
|
665
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + apiKey },
|
|
666
|
+
body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }], max_tokens: 256 }),
|
|
667
|
+
signal: AbortSignal.timeout(15000),
|
|
668
|
+
});
|
|
669
|
+
if (res.ok) {
|
|
670
|
+
const data = await res.json();
|
|
671
|
+
const reply = data.choices?.[0]?.message?.content || data.response || '(no content)';
|
|
672
|
+
console.log(chalk.green(' Response:\n'));
|
|
673
|
+
console.log(chalk.white(' ' + reply.split('\n').join('\n ')));
|
|
674
|
+
console.log('');
|
|
675
|
+
} else if (res.status === 401) {
|
|
676
|
+
console.log(chalk.red(' \u274C Invalid API key\n'));
|
|
677
|
+
} else {
|
|
678
|
+
const text = await res.text().catch(() => '');
|
|
679
|
+
console.log(chalk.yellow(' \u26A0 HTTP ' + res.status + ': ' + text.slice(0, 100) + '\n'));
|
|
680
|
+
}
|
|
681
|
+
} catch (err) {
|
|
682
|
+
console.log(chalk.red(' \u274C ' + err.message + '\n'));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async function inferAuth(provider) {
|
|
687
|
+
const config = getConfig();
|
|
688
|
+
const providers = [
|
|
689
|
+
{ name: 'openai', key: config.openaiApiKey || process.env.OPENAI_API_KEY, url: 'https://api.openai.com/v1/models' },
|
|
690
|
+
{ name: 'anthropic', key: config.anthropicApiKey || process.env.ANTHROPIC_API_KEY, url: null },
|
|
691
|
+
{ name: 'groq', key: config.groqApiKey || process.env.GROQ_API_KEY, url: 'https://api.groq.com/openai/v1/models' },
|
|
692
|
+
{ name: 'deepseek', key: config.deepseekApiKey || process.env.DEEPSEEK_API_KEY, url: 'https://api.deepseek.com/v1/models' },
|
|
693
|
+
{ name: 'mistral', key: config.mistralApiKey || process.env.MISTRAL_API_KEY, url: 'https://api.mistral.ai/v1/models' },
|
|
694
|
+
{ name: 'perplexity', key: config.perplexityApiKey || process.env.PERPLEXITY_API_KEY, url: 'https://api.perplexity.ai/v1/models' },
|
|
695
|
+
{ name: 'together', key: config.togetherApiKey || process.env.TOGETHER_API_KEY, url: 'https://api.together.xyz/v1/models' },
|
|
696
|
+
{ name: 'xai', key: config.xaiApiKey || process.env.XAI_API_KEY, url: 'https://api.x.ai/v1/models' },
|
|
697
|
+
{ name: 'elevenlabs', key: config.elevenlabsApiKey || process.env.ELEVENLABS_API_KEY, url: null },
|
|
698
|
+
{ name: 'deepgram', key: config.deepgramApiKey || process.env.DEEPGRAM_API_KEY, url: null },
|
|
699
|
+
{ name: 'fal', key: config.falKey || process.env.FAL_KEY, url: null },
|
|
700
|
+
{ name: 'openrouter', key: config.openrouterApiKey || process.env.OPENROUTER_API_KEY, url: 'https://openrouter.ai/api/v1/models' },
|
|
701
|
+
];
|
|
702
|
+
if (provider) {
|
|
703
|
+
const p = providers.find(x => x.name === provider.toLowerCase());
|
|
704
|
+
if (!p) {
|
|
705
|
+
console.log(chalk.red('\n \u274C Unknown provider: ' + provider + '\n'));
|
|
706
|
+
console.log(chalk.gray(' Available: ' + providers.map(x => x.name).join(', ') + '\n'));
|
|
707
|
+
process.exit(1);
|
|
708
|
+
}
|
|
709
|
+
console.log(chalk.cyan('\n Testing ' + p.name + ' authentication...\n'));
|
|
710
|
+
if (!p.key) {
|
|
711
|
+
console.log(chalk.yellow(' \u26A0 No API key found\n'));
|
|
712
|
+
console.log(chalk.gray(' Set it: natureco secrets set ' + p.name + 'ApiKey <key>\n'));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (!p.url) {
|
|
716
|
+
console.log(chalk.green(' \u2713 API key found (no test endpoint available)\n'));
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
const res = await fetch(p.url, {
|
|
721
|
+
headers: { Authorization: 'Bearer ' + p.key },
|
|
722
|
+
signal: AbortSignal.timeout(5000),
|
|
723
|
+
});
|
|
724
|
+
if (res.ok) {
|
|
725
|
+
console.log(chalk.green(' \u2713 Authentication successful\n'));
|
|
726
|
+
} else if (res.status === 401) {
|
|
727
|
+
console.log(chalk.red(' \u2717 Invalid API key\n'));
|
|
728
|
+
} else {
|
|
729
|
+
console.log(chalk.yellow(' \u26A0 HTTP ' + res.status + '\n'));
|
|
730
|
+
}
|
|
731
|
+
} catch (err) {
|
|
732
|
+
console.log(chalk.yellow(' \u26A0 ' + err.message + '\n'));
|
|
733
|
+
}
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
console.log(chalk.cyan('\n Testing all providers...\n'));
|
|
737
|
+
for (const p of providers) {
|
|
738
|
+
if (!p.key) {
|
|
739
|
+
console.log(chalk.gray(' \u25CB ' + p.name + ': not configured'));
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (!p.url) {
|
|
743
|
+
console.log(chalk.green(' \u2713 ' + p.name + ': API key found'));
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
try {
|
|
747
|
+
const res = await fetch(p.url, {
|
|
748
|
+
headers: { Authorization: 'Bearer ' + p.key },
|
|
749
|
+
signal: AbortSignal.timeout(5000),
|
|
750
|
+
});
|
|
751
|
+
if (res.ok) {
|
|
752
|
+
console.log(chalk.green(' \u2713 ' + p.name + ': authenticated'));
|
|
753
|
+
} else if (res.status === 401) {
|
|
754
|
+
console.log(chalk.red(' \u2717 ' + p.name + ': invalid key'));
|
|
755
|
+
} else {
|
|
756
|
+
console.log(chalk.yellow(' \u26A0 ' + p.name + ': HTTP ' + res.status));
|
|
757
|
+
}
|
|
758
|
+
} catch (err) {
|
|
759
|
+
console.log(chalk.yellow(' \u26A0 ' + p.name + ': ' + err.message));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
console.log('');
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
async function inferImage(prompt) {
|
|
766
|
+
if (!prompt) {
|
|
767
|
+
console.log(chalk.red('\n \u274C Prompt gerekli\n'));
|
|
768
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer image <prompt>\n'));
|
|
769
|
+
process.exit(1);
|
|
770
|
+
}
|
|
771
|
+
console.log(chalk.cyan('\n Image Generation\n'));
|
|
772
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
773
|
+
console.log(chalk.gray(' Prompt: ') + chalk.white(prompt));
|
|
774
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 image generation not available'));
|
|
775
|
+
console.log(chalk.gray(' To enable: configure a provider with image generation support'));
|
|
776
|
+
console.log(chalk.gray(' (e.g., OpenAI DALL-E, Together AI, or Fal.ai)\n'));
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
async function inferAudio(prompt) {
|
|
780
|
+
if (!prompt) {
|
|
781
|
+
console.log(chalk.red('\n \u274C Prompt gerekli\n'));
|
|
782
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer audio <prompt>\n'));
|
|
783
|
+
process.exit(1);
|
|
784
|
+
}
|
|
785
|
+
console.log(chalk.cyan('\n Audio Generation\n'));
|
|
786
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
787
|
+
console.log(chalk.gray(' Prompt: ') + chalk.white(prompt));
|
|
788
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 audio generation not available'));
|
|
789
|
+
console.log(chalk.gray(' Providers: Suno, Udio, ElevenLabs\n'));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async function inferTts(text) {
|
|
793
|
+
if (!text) {
|
|
794
|
+
console.log(chalk.red('\n \u274C Text gerekli\n'));
|
|
795
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer tts <text>\n'));
|
|
796
|
+
process.exit(1);
|
|
797
|
+
}
|
|
798
|
+
console.log(chalk.cyan('\n Text-to-Speech\n'));
|
|
799
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
800
|
+
console.log(chalk.gray(' Text : ') + chalk.white(text.slice(0, 80)));
|
|
801
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 TTS not available'));
|
|
802
|
+
console.log(chalk.gray(' Providers: ElevenLabs, OpenAI TTS\n'));
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
async function inferVideo(prompt) {
|
|
806
|
+
if (!prompt) {
|
|
807
|
+
console.log(chalk.red('\n \u274C Prompt gerekli\n'));
|
|
808
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer video <prompt>\n'));
|
|
809
|
+
process.exit(1);
|
|
810
|
+
}
|
|
811
|
+
console.log(chalk.cyan('\n Video Generation\n'));
|
|
812
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
813
|
+
console.log(chalk.gray(' Prompt: ') + chalk.white(prompt));
|
|
814
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 video generation not available'));
|
|
815
|
+
console.log(chalk.gray(' Providers: Runway, Pika, Synthesia\n'));
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
async function inferWeb(query) {
|
|
819
|
+
if (!query) {
|
|
820
|
+
console.log(chalk.red('\n \u274C Query gerekli\n'));
|
|
821
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer web <query>\n'));
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
console.log(chalk.cyan('\n Web Search via AI\n'));
|
|
825
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
826
|
+
console.log(chalk.gray(' Query: ') + chalk.white(query));
|
|
827
|
+
const config = getConfig();
|
|
828
|
+
const apiKey = config.tavilyApiKey || process.env.TAVILY_API_KEY;
|
|
829
|
+
if (apiKey) {
|
|
830
|
+
try {
|
|
831
|
+
const res = await fetch('https://api.tavily.com/search', {
|
|
832
|
+
method: 'POST',
|
|
833
|
+
headers: { 'Content-Type': 'application/json' },
|
|
834
|
+
body: JSON.stringify({ api_key: apiKey, query, max_results: 5 }),
|
|
835
|
+
signal: AbortSignal.timeout(10000),
|
|
836
|
+
});
|
|
837
|
+
if (res.ok) {
|
|
838
|
+
const data = await res.json();
|
|
839
|
+
const results = data.results || [];
|
|
840
|
+
console.log(chalk.green('\n Results (' + results.length + '):\n'));
|
|
841
|
+
for (const r of results.slice(0, 5)) {
|
|
842
|
+
console.log(chalk.cyan(' ' + (r.title || 'Untitled')));
|
|
843
|
+
console.log(chalk.gray(' ' + (r.url || '')));
|
|
844
|
+
console.log(chalk.white(' ' + (r.content || '').slice(0, 120)));
|
|
845
|
+
console.log('');
|
|
846
|
+
}
|
|
847
|
+
return;
|
|
848
|
+
} else {
|
|
849
|
+
console.log(chalk.yellow(' Search API error: HTTP ' + res.status + '\n'));
|
|
850
|
+
}
|
|
851
|
+
} catch (err) {
|
|
852
|
+
console.log(chalk.yellow(' Search error: ' + err.message + '\n'));
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
console.log(chalk.gray('\n No search API key configured (Tavily).'));
|
|
856
|
+
console.log(chalk.gray(' Set: natureco secrets set tavilyApiKey <key>\n'));
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
async function inferEmbedding(text) {
|
|
861
|
+
if (!text) {
|
|
862
|
+
console.log(chalk.red('\n \u274C Text gerekli\n'));
|
|
863
|
+
console.log(chalk.gray(' Kullan\u0131m: natureco infer embedding <text>\n'));
|
|
864
|
+
process.exit(1);
|
|
865
|
+
}
|
|
866
|
+
const config = getConfig();
|
|
867
|
+
const apiKey = config.openaiApiKey || process.env.OPENAI_API_KEY;
|
|
868
|
+
console.log(chalk.cyan('\n Generate Embedding\n'));
|
|
869
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
870
|
+
console.log(chalk.gray(' Text : ') + chalk.white(text.slice(0, 80)));
|
|
871
|
+
if (apiKey) {
|
|
872
|
+
try {
|
|
873
|
+
const res = await fetch('https://api.openai.com/v1/embeddings', {
|
|
874
|
+
method: 'POST',
|
|
875
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + apiKey },
|
|
876
|
+
body: JSON.stringify({ model: 'text-embedding-3-small', input: text }),
|
|
877
|
+
signal: AbortSignal.timeout(10000),
|
|
878
|
+
});
|
|
879
|
+
if (res.ok) {
|
|
880
|
+
const data = await res.json();
|
|
881
|
+
const embedding = data.data?.[0]?.embedding || [];
|
|
882
|
+
console.log(chalk.green('\n \u2713 Embedding generated\n'));
|
|
883
|
+
console.log(chalk.gray(' Dimensions: ') + chalk.white(embedding.length));
|
|
884
|
+
console.log(chalk.gray(' Vector : ') + chalk.gray('[' + embedding.slice(0, 5).map(v => v.toFixed(4)).join(', ') + ', ...]'));
|
|
885
|
+
console.log('');
|
|
886
|
+
return;
|
|
887
|
+
} else {
|
|
888
|
+
console.log(chalk.yellow(' Embedding API error: HTTP ' + res.status + '\n'));
|
|
889
|
+
}
|
|
890
|
+
} catch (err) {
|
|
891
|
+
console.log(chalk.yellow(' Embedding error: ' + err.message + '\n'));
|
|
892
|
+
}
|
|
893
|
+
} else {
|
|
894
|
+
console.log(chalk.gray('\n No OpenAI API key configured.'));
|
|
895
|
+
console.log(chalk.gray(' Set: natureco secrets set openaiApiKey <key>\n'));
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// ---------------------------------------------------------------------------
|
|
900
|
+
// New functions
|
|
901
|
+
// ---------------------------------------------------------------------------
|
|
902
|
+
|
|
903
|
+
async function inferList() {
|
|
904
|
+
console.log(chalk.cyan('\n Available Model Providers\n'));
|
|
905
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
906
|
+
|
|
907
|
+
const knownProviders = [
|
|
908
|
+
{ name: 'openai', description: 'GPT-4, GPT-4o, DALL-E, Whisper, TTS, Embeddings' },
|
|
909
|
+
{ name: 'anthropic', description: 'Claude Opus, Sonnet, Haiku' },
|
|
910
|
+
{ name: 'groq', description: 'Llama, Mixtral, Gemma (fast inference)' },
|
|
911
|
+
{ name: 'deepseek', description: 'DeepSeek Chat, Coder, Reasoner' },
|
|
912
|
+
{ name: 'mistral', description: 'Mistral Large, Medium, Small, Codestral' },
|
|
913
|
+
{ name: 'xai', description: 'Grok Beta, Grok Vision' },
|
|
914
|
+
{ name: 'perplexity', description: 'Sonar Pro, Sonar, Sonar Reasoning' },
|
|
915
|
+
{ name: 'together', description: 'Llama, Mixtral, DeepSeek, Qwen' },
|
|
916
|
+
{ name: 'openrouter', description: 'Unified API for many providers' },
|
|
917
|
+
{ name: 'elevenlabs', description: 'Text-to-Speech, Voice Cloning' },
|
|
918
|
+
{ name: 'deepgram', description: 'Speech-to-Text, Audio Intelligence' },
|
|
919
|
+
{ name: 'fal', description: 'Image/Video generation models' },
|
|
920
|
+
];
|
|
921
|
+
|
|
922
|
+
const config = getConfig();
|
|
923
|
+
for (const p of knownProviders) {
|
|
924
|
+
const keyName = p.name + 'ApiKey';
|
|
925
|
+
const keyNameAlt = p.name === 'fal' ? 'falKey' : null;
|
|
926
|
+
const hasKey = config[keyName] || config[keyNameAlt] || process.env[keyName.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase()];
|
|
927
|
+
const icon = hasKey ? chalk.green('\u25CF') : chalk.gray('\u25CB');
|
|
928
|
+
console.log(' ' + icon + ' ' + chalk.white(p.name.padEnd(14)) + chalk.gray(p.description));
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
console.log(chalk.gray('\n Inspect: ') + chalk.cyan('natureco infer inspect <provider>'));
|
|
932
|
+
console.log();
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
async function inferInspect(provider) {
|
|
936
|
+
const config = getConfig();
|
|
937
|
+
const providerMap = {
|
|
938
|
+
openai: { name: 'OpenAI', url: 'https://api.openai.com/v1/models', key: config.openaiApiKey, docs: 'https://platform.openai.com/docs' },
|
|
939
|
+
anthropic: { name: 'Anthropic', url: null, key: config.anthropicApiKey, docs: 'https://docs.anthropic.com' },
|
|
940
|
+
groq: { name: 'Groq', url: 'https://api.groq.com/openai/v1/models', key: config.groqApiKey, docs: 'https://console.groq.com/docs' },
|
|
941
|
+
deepseek: { name: 'DeepSeek', url: 'https://api.deepseek.com/v1/models', key: config.deepseekApiKey, docs: 'https://platform.deepseek.com' },
|
|
942
|
+
mistral: { name: 'Mistral', url: 'https://api.mistral.ai/v1/models', key: config.mistralApiKey, docs: 'https://docs.mistral.ai' },
|
|
943
|
+
xai: { name: 'xAI', url: 'https://api.x.ai/v1/models', key: config.xaiApiKey, docs: 'https://docs.x.ai' },
|
|
944
|
+
perplexity: { name: 'Perplexity', url: 'https://api.perplexity.ai/v1/models', key: config.perplexityApiKey, docs: 'https://docs.perplexity.ai' },
|
|
945
|
+
together: { name: 'Together', url: 'https://api.together.xyz/v1/models', key: config.togetherApiKey, docs: 'https://docs.together.ai' },
|
|
946
|
+
openrouter: { name: 'OpenRouter', url: 'https://openrouter.ai/api/v1/models', key: config.openrouterApiKey, docs: 'https://openrouter.ai/docs' },
|
|
947
|
+
elevenlabs: { name: 'ElevenLabs', url: null, key: config.elevenlabsApiKey, docs: 'https://elevenlabs.io/docs' },
|
|
948
|
+
deepgram: { name: 'Deepgram', url: null, key: config.deepgramApiKey, docs: 'https://developers.deepgram.com' },
|
|
949
|
+
fal: { name: 'Fal.ai', url: null, key: config.falKey, docs: 'https://fal.ai/docs' },
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
const p = providerMap[provider.toLowerCase()];
|
|
953
|
+
if (!p) {
|
|
954
|
+
console.log(chalk.red('\n \u274C Unknown provider: ' + provider + '\n'));
|
|
955
|
+
console.log(chalk.gray(' Known providers: ' + Object.keys(providerMap).join(', ') + '\n'));
|
|
956
|
+
process.exit(1);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
console.log(chalk.cyan('\n Provider: ' + p.name + '\n'));
|
|
960
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
961
|
+
console.log(chalk.gray(' Name : ') + chalk.white(p.name));
|
|
962
|
+
console.log(chalk.gray(' API Key : ') + (p.key ? chalk.green('\u2713 configured') : chalk.gray('not set')));
|
|
963
|
+
console.log(chalk.gray(' API URL : ') + chalk.white(p.url || '(no model list endpoint)'));
|
|
964
|
+
console.log(chalk.gray(' Docs : ') + chalk.cyan(p.docs));
|
|
965
|
+
console.log();
|
|
966
|
+
|
|
967
|
+
if (p.url && p.key) {
|
|
968
|
+
console.log(chalk.gray(' Fetching models...'));
|
|
969
|
+
try {
|
|
970
|
+
const res = await fetch(p.url, {
|
|
971
|
+
headers: { Authorization: 'Bearer ' + p.key },
|
|
972
|
+
signal: AbortSignal.timeout(5000),
|
|
973
|
+
});
|
|
974
|
+
if (res.ok) {
|
|
975
|
+
const data = await res.json();
|
|
976
|
+
const models = (data.data || data.models || []).map(m => m.id || m);
|
|
977
|
+
console.log(chalk.green(' \u2713 ' + models.length + ' models available\n'));
|
|
978
|
+
for (const m of models.slice(0, 10)) {
|
|
979
|
+
console.log(chalk.white(' - ' + m));
|
|
980
|
+
}
|
|
981
|
+
if (models.length > 10) {
|
|
982
|
+
console.log(chalk.gray(' ... and ' + (models.length - 10) + ' more'));
|
|
983
|
+
}
|
|
984
|
+
} else if (res.status === 401) {
|
|
985
|
+
console.log(chalk.red(' \u2717 Invalid API key\n'));
|
|
986
|
+
} else {
|
|
987
|
+
console.log(chalk.yellow(' \u26A0 HTTP ' + res.status + '\n'));
|
|
988
|
+
}
|
|
989
|
+
} catch (err) {
|
|
990
|
+
console.log(chalk.yellow(' \u26A0 ' + err.message + '\n'));
|
|
991
|
+
}
|
|
992
|
+
} else if (!p.key) {
|
|
993
|
+
console.log(chalk.gray(' Set API key via: natureco secrets set ' + provider + 'ApiKey <key>\n'));
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
async function inferModelList() {
|
|
998
|
+
const config = getConfig();
|
|
999
|
+
const providerUrl = config.providerUrl || '';
|
|
1000
|
+
const apiKey = config.providerApiKey || '';
|
|
1001
|
+
|
|
1002
|
+
console.log(chalk.cyan('\n Available Models\n'));
|
|
1003
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1004
|
+
|
|
1005
|
+
if (apiKey && providerUrl) {
|
|
1006
|
+
try {
|
|
1007
|
+
const endpoint = providerUrl.includes('/v1/') ? providerUrl : providerUrl.replace(/\/$/, '') + '/v1/models';
|
|
1008
|
+
const res = await fetch(endpoint, {
|
|
1009
|
+
headers: { Authorization: 'Bearer ' + apiKey },
|
|
1010
|
+
signal: AbortSignal.timeout(5000),
|
|
1011
|
+
});
|
|
1012
|
+
if (res.ok) {
|
|
1013
|
+
const data = await res.json();
|
|
1014
|
+
const models = (data.data || data.models || []).map(m => m.id || m);
|
|
1015
|
+
if (models.length === 0) {
|
|
1016
|
+
console.log(chalk.gray(' No models returned by provider\n'));
|
|
1017
|
+
} else {
|
|
1018
|
+
for (const m of models) {
|
|
1019
|
+
const features = [];
|
|
1020
|
+
for (const entry of MODEL_FEATURE_MAP) {
|
|
1021
|
+
if (m.toLowerCase().includes(entry.pattern)) {
|
|
1022
|
+
features.push(entry.features.join(', '));
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
const tag = features.length > 0 ? chalk.gray(' [' + features[0] + ']') : '';
|
|
1026
|
+
console.log(chalk.white(' - ' + m) + ' ' + tag);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
console.log();
|
|
1030
|
+
return;
|
|
1031
|
+
} else if (res.status === 401) {
|
|
1032
|
+
console.log(chalk.red(' \u2717 Invalid API key\n'));
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
console.log(chalk.yellow(' \u26A0 ' + err.message + '\n'));
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const known = getKnownModels(providerUrl);
|
|
1042
|
+
if (known.length > 0) {
|
|
1043
|
+
console.log(chalk.gray(' Known models for current provider:\n'));
|
|
1044
|
+
for (const m of known) {
|
|
1045
|
+
console.log(chalk.white(' - ' + m));
|
|
1046
|
+
}
|
|
1047
|
+
console.log();
|
|
1048
|
+
} else {
|
|
1049
|
+
console.log(chalk.gray(' No models found. Configure a provider with:\n'));
|
|
1050
|
+
console.log(chalk.cyan(' natureco setup\n'));
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
async function inferModelInspect(model) {
|
|
1055
|
+
console.log(chalk.cyan('\n Model: ' + model + '\n'));
|
|
1056
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1057
|
+
|
|
1058
|
+
const id = model.toLowerCase();
|
|
1059
|
+
const matchedFeatures = [];
|
|
1060
|
+
for (const entry of MODEL_FEATURE_MAP) {
|
|
1061
|
+
if (id.includes(entry.pattern)) {
|
|
1062
|
+
matchedFeatures.push(entry.features);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
const allFeatures = [...new Set(matchedFeatures.flat())];
|
|
1066
|
+
|
|
1067
|
+
console.log(chalk.gray(' Name : ') + chalk.white(model));
|
|
1068
|
+
console.log(chalk.gray(' Capabilities: ') + (allFeatures.length > 0 ? chalk.green(allFeatures.join(', ')) : chalk.gray('generic model')));
|
|
1069
|
+
console.log();
|
|
1070
|
+
|
|
1071
|
+
const config = getConfig();
|
|
1072
|
+
const providerUrl = config.providerUrl || '';
|
|
1073
|
+
const apiKey = config.providerApiKey || '';
|
|
1074
|
+
if (apiKey && providerUrl) {
|
|
1075
|
+
console.log(chalk.gray(' Testing chat endpoint...'));
|
|
1076
|
+
try {
|
|
1077
|
+
const testUrl = providerUrl.includes('/v1/') ? providerUrl : providerUrl.replace(/\/$/, '') + '/v1/chat/completions';
|
|
1078
|
+
const res = await fetch(testUrl, {
|
|
1079
|
+
method: 'POST',
|
|
1080
|
+
headers: { Authorization: 'Bearer ' + apiKey, 'Content-Type': 'application/json' },
|
|
1081
|
+
body: JSON.stringify({ model, messages: [{ role: 'user', content: 'Respond with word: ok' }], max_tokens: 5 }),
|
|
1082
|
+
signal: AbortSignal.timeout(8000),
|
|
1083
|
+
});
|
|
1084
|
+
if (res.ok) {
|
|
1085
|
+
console.log(chalk.green(' \u2713 Model responds\n'));
|
|
1086
|
+
} else if (res.status === 404) {
|
|
1087
|
+
console.log(chalk.yellow(' \u26A0 Model not found or not accessible\n'));
|
|
1088
|
+
} else {
|
|
1089
|
+
console.log(chalk.yellow(' \u26A0 HTTP ' + res.status + '\n'));
|
|
1090
|
+
}
|
|
1091
|
+
} catch (err) {
|
|
1092
|
+
console.log(chalk.yellow(' \u26A0 ' + err.message + '\n'));
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
async function inferModelProviders() {
|
|
1098
|
+
return inferList();
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
async function inferModelAuthLogin(provider) {
|
|
1102
|
+
const config = getConfig();
|
|
1103
|
+
const key = config[provider + 'ApiKey'] || process.env[provider.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase() + '_API_KEY'];
|
|
1104
|
+
if (key) {
|
|
1105
|
+
console.log(chalk.green('\n \u2713 Already authenticated with ' + provider + '\n'));
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
console.log(chalk.cyan('\n Authenticating with ' + provider + '...\n'));
|
|
1109
|
+
console.log(chalk.gray(' Use: ') + chalk.white('natureco secrets set ' + provider + 'ApiKey <key>'));
|
|
1110
|
+
console.log(chalk.gray(' Or: ') + chalk.white('natureco infer auth add ' + provider + ' <key>'));
|
|
1111
|
+
console.log();
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
async function inferModelAuthLogout(provider) {
|
|
1115
|
+
console.log(chalk.yellow('\n \u26A0 Logout from ' + provider + '...\n'));
|
|
1116
|
+
console.log(chalk.gray(' Remove API key from secrets:'));
|
|
1117
|
+
console.log(chalk.white(' natureco secrets delete ' + provider + 'ApiKey'));
|
|
1118
|
+
console.log();
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
async function inferModelAuthStatus() {
|
|
1122
|
+
const config = getConfig();
|
|
1123
|
+
console.log(chalk.cyan('\n Model Auth Status\n'));
|
|
1124
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1125
|
+
|
|
1126
|
+
const providers = [
|
|
1127
|
+
{ name: 'openai', key: config.openaiApiKey || process.env.OPENAI_API_KEY },
|
|
1128
|
+
{ name: 'anthropic', key: config.anthropicApiKey || process.env.ANTHROPIC_API_KEY },
|
|
1129
|
+
{ name: 'groq', key: config.groqApiKey || process.env.GROQ_API_KEY },
|
|
1130
|
+
{ name: 'deepseek', key: config.deepseekApiKey || process.env.DEEPSEEK_API_KEY },
|
|
1131
|
+
{ name: 'mistral', key: config.mistralApiKey || process.env.MISTRAL_API_KEY },
|
|
1132
|
+
{ name: 'xai', key: config.xaiApiKey || process.env.XAI_API_KEY },
|
|
1133
|
+
{ name: 'perplexity', key: config.perplexityApiKey || process.env.PERPLEXITY_API_KEY },
|
|
1134
|
+
{ name: 'together', key: config.togetherApiKey || process.env.TOGETHER_API_KEY },
|
|
1135
|
+
];
|
|
1136
|
+
|
|
1137
|
+
for (const p of providers) {
|
|
1138
|
+
const icon = p.key ? chalk.green('\u25CF') : chalk.gray('\u25CB');
|
|
1139
|
+
const status = p.key ? chalk.green('authenticated') : chalk.gray('not configured');
|
|
1140
|
+
console.log(' ' + icon + ' ' + chalk.white(p.name.padEnd(14)) + status);
|
|
1141
|
+
}
|
|
1142
|
+
console.log();
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// ---------------------------------------------------------------------------
|
|
1146
|
+
// Image subcommands
|
|
1147
|
+
// ---------------------------------------------------------------------------
|
|
1148
|
+
|
|
1149
|
+
async function inferImageGenerate(prompt) {
|
|
1150
|
+
console.log(chalk.cyan('\n Image Generation\n'));
|
|
1151
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1152
|
+
console.log(chalk.gray(' Prompt: ') + chalk.white(prompt));
|
|
1153
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 image generation not available'));
|
|
1154
|
+
console.log(chalk.gray(' To generate images, configure DALL-E, Stable Diffusion, or Fal.ai\n'));
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
async function inferImageEdit(path, prompt) {
|
|
1158
|
+
console.log(chalk.cyan('\n Image Edit\n'));
|
|
1159
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1160
|
+
console.log(chalk.gray(' Image : ') + chalk.white(path));
|
|
1161
|
+
console.log(chalk.gray(' Edit : ') + chalk.white(prompt));
|
|
1162
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 image editing not available'));
|
|
1163
|
+
console.log();
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
async function inferImageDescribe(imagePath) {
|
|
1167
|
+
console.log(chalk.cyan('\n Image Description\n'));
|
|
1168
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1169
|
+
console.log(chalk.gray(' Path : ') + chalk.white(imagePath));
|
|
1170
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 image description not available'));
|
|
1171
|
+
console.log(chalk.gray(' Requires a vision-capable model (GPT-4o, Claude, Llama-3.2)\n'));
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
async function inferImageDescribeMany(dir) {
|
|
1175
|
+
console.log(chalk.cyan('\n Describe Images in Directory\n'));
|
|
1176
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1177
|
+
console.log(chalk.gray(' Dir : ') + chalk.white(dir));
|
|
1178
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 batch description not available'));
|
|
1179
|
+
console.log();
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
async function inferImageProviders() {
|
|
1183
|
+
console.log(chalk.cyan('\n Image Providers\n'));
|
|
1184
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1185
|
+
const providers = [
|
|
1186
|
+
{ name: 'OpenAI DALL-E', models: 'dall-e-3, dall-e-2' },
|
|
1187
|
+
{ name: 'Stable Diffusion', models: 'SDXL, SD3, Flux' },
|
|
1188
|
+
{ name: 'Fal.ai', models: 'Flux, Stable Diffusion, Pika' },
|
|
1189
|
+
{ name: 'Together AI', models: 'Stable Diffusion, Flux' },
|
|
1190
|
+
];
|
|
1191
|
+
for (const p of providers) {
|
|
1192
|
+
console.log(chalk.white(' \u25CF ' + p.name.padEnd(22)) + chalk.gray(p.models));
|
|
1193
|
+
}
|
|
1194
|
+
console.log();
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// ---------------------------------------------------------------------------
|
|
1198
|
+
// Audio subcommands
|
|
1199
|
+
// ---------------------------------------------------------------------------
|
|
1200
|
+
|
|
1201
|
+
async function inferAudioTranscribe(audioPath) {
|
|
1202
|
+
const config = getConfig();
|
|
1203
|
+
const apiKey = config.openaiApiKey || process.env.OPENAI_API_KEY;
|
|
1204
|
+
console.log(chalk.cyan('\n Audio Transcription\n'));
|
|
1205
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1206
|
+
console.log(chalk.gray(' File : ') + chalk.white(audioPath));
|
|
1207
|
+
if (apiKey) {
|
|
1208
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 transcription via Whisper not available in CLI'));
|
|
1209
|
+
console.log(chalk.gray(' Use: ') + chalk.white('curl https://api.openai.com/v1/audio/transcriptions ...'));
|
|
1210
|
+
console.log();
|
|
1211
|
+
} else {
|
|
1212
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('not configured'));
|
|
1213
|
+
console.log(chalk.gray(' Set: natureco secrets set openaiApiKey <key>\n'));
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
async function inferAudioProviders() {
|
|
1218
|
+
console.log(chalk.cyan('\n Audio Providers\n'));
|
|
1219
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1220
|
+
const providers = [
|
|
1221
|
+
{ name: 'OpenAI Whisper', feature: 'Speech-to-Text' },
|
|
1222
|
+
{ name: 'Deepgram', feature: 'Speech-to-Text, Audio Intelligence' },
|
|
1223
|
+
{ name: 'ElevenLabs', feature: 'Speech-to-Text, Voice Design' },
|
|
1224
|
+
];
|
|
1225
|
+
for (const p of providers) {
|
|
1226
|
+
console.log(chalk.white(' \u25CF ' + p.name.padEnd(22)) + chalk.gray(p.feature));
|
|
1227
|
+
}
|
|
1228
|
+
console.log();
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// ---------------------------------------------------------------------------
|
|
1232
|
+
// TTS subcommands
|
|
1233
|
+
// ---------------------------------------------------------------------------
|
|
1234
|
+
|
|
1235
|
+
async function inferTtsConvert(text) {
|
|
1236
|
+
const config = getConfig();
|
|
1237
|
+
const state = readState();
|
|
1238
|
+
const ttsProvider = state.ttsProvider || 'elevenlabs';
|
|
1239
|
+
console.log(chalk.cyan('\n Text-to-Speech Conversion\n'));
|
|
1240
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1241
|
+
console.log(chalk.gray(' Text : ') + chalk.white(text.slice(0, 80)));
|
|
1242
|
+
console.log(chalk.gray(' Provider: ') + chalk.white(ttsProvider));
|
|
1243
|
+
console.log(chalk.gray(' Status : ') + chalk.yellow('MOCK \u2014 TTS conversion not available'));
|
|
1244
|
+
console.log(chalk.gray(' Providers: ElevenLabs, OpenAI TTS\n'));
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
async function inferTtsVoices() {
|
|
1248
|
+
console.log(chalk.cyan('\n Available TTS Voices\n'));
|
|
1249
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1250
|
+
console.log(chalk.gray(' ElevenLabs: Rachel, Adam, Bella, Joshua, etc.'));
|
|
1251
|
+
console.log(chalk.gray(' OpenAI : alloy, echo, fable, nova, shimmer'));
|
|
1252
|
+
console.log();
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
async function inferTtsProviders() {
|
|
1256
|
+
console.log(chalk.cyan('\n TTS Providers\n'));
|
|
1257
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1258
|
+
console.log(chalk.white(' \u25CF ElevenLabs ') + chalk.gray('High-quality voice synthesis'));
|
|
1259
|
+
console.log(chalk.white(' \u25CF OpenAI TTS ') + chalk.gray('GPT-4o integrated TTS'));
|
|
1260
|
+
console.log();
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
async function inferTtsStatus() {
|
|
1264
|
+
const state = readState();
|
|
1265
|
+
const enabled = state.ttsEnabled !== false;
|
|
1266
|
+
const provider = state.ttsProvider || 'elevenlabs';
|
|
1267
|
+
console.log(chalk.cyan('\n TTS Status\n'));
|
|
1268
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1269
|
+
console.log(chalk.gray(' Enabled : ') + (enabled ? chalk.green('yes') : chalk.red('no')));
|
|
1270
|
+
console.log(chalk.gray(' Provider: ') + chalk.white(provider));
|
|
1271
|
+
console.log();
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
async function inferTtsEnable() {
|
|
1275
|
+
const state = readState();
|
|
1276
|
+
state.ttsEnabled = true;
|
|
1277
|
+
writeState(state);
|
|
1278
|
+
console.log(chalk.green('\n \u2713 TTS enabled\n'));
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
async function inferTtsDisable() {
|
|
1282
|
+
const state = readState();
|
|
1283
|
+
state.ttsEnabled = false;
|
|
1284
|
+
writeState(state);
|
|
1285
|
+
console.log(chalk.yellow('\n \u26A0 TTS disabled\n'));
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
async function inferTtsSetProvider(name) {
|
|
1289
|
+
const valid = ['elevenlabs', 'openai'];
|
|
1290
|
+
if (!valid.includes(name.toLowerCase())) {
|
|
1291
|
+
console.log(chalk.red('\n \u274C Invalid TTS provider. Valid: ' + valid.join(', ') + '\n'));
|
|
1292
|
+
process.exit(1);
|
|
1293
|
+
}
|
|
1294
|
+
const state = readState();
|
|
1295
|
+
state.ttsProvider = name.toLowerCase();
|
|
1296
|
+
writeState(state);
|
|
1297
|
+
console.log(chalk.green('\n \u2713 TTS provider set to ' + name.toLowerCase() + '\n'));
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// ---------------------------------------------------------------------------
|
|
1301
|
+
// Video subcommands
|
|
1302
|
+
// ---------------------------------------------------------------------------
|
|
1303
|
+
|
|
1304
|
+
async function inferVideoGenerate(prompt) {
|
|
1305
|
+
console.log(chalk.cyan('\n Video Generation\n'));
|
|
1306
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1307
|
+
console.log(chalk.gray(' Prompt: ') + chalk.white(prompt));
|
|
1308
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 video generation not available'));
|
|
1309
|
+
console.log(chalk.gray(' Providers: Runway, Pika, Synthesia\n'));
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
async function inferVideoDescribe(videoPath) {
|
|
1313
|
+
console.log(chalk.cyan('\n Video Description\n'));
|
|
1314
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1315
|
+
console.log(chalk.gray(' Path : ') + chalk.white(videoPath));
|
|
1316
|
+
console.log(chalk.gray(' Status: ') + chalk.yellow('MOCK \u2014 video description not available'));
|
|
1317
|
+
console.log();
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
async function inferVideoProviders() {
|
|
1321
|
+
console.log(chalk.cyan('\n Video Providers\n'));
|
|
1322
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1323
|
+
const providers = [
|
|
1324
|
+
{ name: 'Runway', feature: 'Gen-2, Gen-3 Alpha' },
|
|
1325
|
+
{ name: 'Pika', feature: 'Pika 2.0' },
|
|
1326
|
+
{ name: 'Synthesia', feature: 'AI Avatars' },
|
|
1327
|
+
];
|
|
1328
|
+
for (const p of providers) {
|
|
1329
|
+
console.log(chalk.white(' \u25CF ' + p.name.padEnd(14)) + chalk.gray(p.feature));
|
|
1330
|
+
}
|
|
1331
|
+
console.log();
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// ---------------------------------------------------------------------------
|
|
1335
|
+
// Web subcommands
|
|
1336
|
+
// ---------------------------------------------------------------------------
|
|
1337
|
+
|
|
1338
|
+
async function inferWebSearch(query) {
|
|
1339
|
+
return inferWeb(query);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
async function inferWebFetch(url) {
|
|
1343
|
+
console.log(chalk.cyan('\n Web Fetch\n'));
|
|
1344
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1345
|
+
console.log(chalk.gray(' URL: ') + chalk.white(url));
|
|
1346
|
+
try {
|
|
1347
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
|
|
1348
|
+
if (res.ok) {
|
|
1349
|
+
const text = await res.text();
|
|
1350
|
+
const preview = text.replace(/<[^>]+>/g, '').trim().slice(0, 500);
|
|
1351
|
+
console.log(chalk.green('\n \u2713 Fetched ' + (text.length + ' bytes')));
|
|
1352
|
+
console.log(chalk.gray('\n Preview:\n'));
|
|
1353
|
+
console.log(chalk.white(' ' + preview));
|
|
1354
|
+
console.log();
|
|
1355
|
+
} else {
|
|
1356
|
+
console.log(chalk.yellow(' \u26A0 HTTP ' + res.status + '\n'));
|
|
1357
|
+
}
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
console.log(chalk.yellow(' \u26A0 ' + err.message + '\n'));
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
async function inferWebProviders() {
|
|
1364
|
+
console.log(chalk.cyan('\n Web Search Providers\n'));
|
|
1365
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1366
|
+
const providers = [
|
|
1367
|
+
{ name: 'Tavily', feature: 'AI-native search API' },
|
|
1368
|
+
{ name: 'Perplexity', feature: 'Sonar search models' },
|
|
1369
|
+
{ name: 'Exa', feature: 'Neural search for AI' },
|
|
1370
|
+
];
|
|
1371
|
+
for (const p of providers) {
|
|
1372
|
+
console.log(chalk.white(' \u25CF ' + p.name.padEnd(14)) + chalk.gray(p.feature));
|
|
1373
|
+
}
|
|
1374
|
+
console.log();
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// ---------------------------------------------------------------------------
|
|
1378
|
+
// Embedding subcommands
|
|
1379
|
+
// ---------------------------------------------------------------------------
|
|
1380
|
+
|
|
1381
|
+
async function inferEmbeddingCreate(text) {
|
|
1382
|
+
return inferEmbedding(text);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
async function inferEmbeddingProviders() {
|
|
1386
|
+
console.log(chalk.cyan('\n Embedding Providers\n'));
|
|
1387
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1388
|
+
const providers = [
|
|
1389
|
+
{ name: 'OpenAI', models: 'text-embedding-3-large, text-embedding-3-small' },
|
|
1390
|
+
{ name: 'Mistral', models: 'mistral-embed' },
|
|
1391
|
+
{ name: 'Together', models: 'BAAI/bge-*' },
|
|
1392
|
+
];
|
|
1393
|
+
for (const p of providers) {
|
|
1394
|
+
console.log(chalk.white(' \u25CF ' + p.name.padEnd(14)) + chalk.gray(p.models));
|
|
1395
|
+
}
|
|
1396
|
+
console.log();
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// ---------------------------------------------------------------------------
|
|
1400
|
+
// Auth subcommands
|
|
1401
|
+
// ---------------------------------------------------------------------------
|
|
1402
|
+
|
|
1403
|
+
async function inferAuthAdd(provider, key) {
|
|
1404
|
+
console.log(chalk.cyan('\n Adding auth for ' + provider + '...\n'));
|
|
1405
|
+
console.log(chalk.gray(' To persist, use:'));
|
|
1406
|
+
console.log(chalk.white(' natureco secrets set ' + provider + 'ApiKey ' + key.slice(0, 8) + '...'));
|
|
1407
|
+
console.log(chalk.gray('\n Or set environment variable:'));
|
|
1408
|
+
console.log(chalk.white(' set ' + provider.toUpperCase() + '_API_KEY=' + key.slice(0, 8) + '...'));
|
|
1409
|
+
console.log();
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
async function inferAuthLogin(provider) {
|
|
1413
|
+
console.log(chalk.cyan('\n OAuth Login: ' + provider + '\n'));
|
|
1414
|
+
console.log(chalk.gray(' Opening browser for OAuth flow...'));
|
|
1415
|
+
console.log(chalk.yellow(' \u26A0 OAuth not yet supported in CLI'));
|
|
1416
|
+
console.log(chalk.gray(' Use: natureco secrets set ' + provider + 'ApiKey <key>\n'));
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
async function inferAuthLoginGithubCopilot() {
|
|
1420
|
+
console.log(chalk.cyan('\n GitHub Copilot Auth\n'));
|
|
1421
|
+
console.log(chalk.gray(' To authenticate with GitHub Copilot:'));
|
|
1422
|
+
console.log(chalk.white(' 1. Visit https://github.com/settings/tokens'));
|
|
1423
|
+
console.log(chalk.white(' 2. Generate a token with copilot scope'));
|
|
1424
|
+
console.log(chalk.white(' 3. Run: natureco infer auth paste-token'));
|
|
1425
|
+
console.log();
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
async function inferAuthSetupToken() {
|
|
1429
|
+
console.log(chalk.cyan('\n Token Authentication Setup\n'));
|
|
1430
|
+
console.log(chalk.gray(' To set up token-based auth:'));
|
|
1431
|
+
console.log(chalk.white(' natureco infer auth add <provider> <token>'));
|
|
1432
|
+
console.log(chalk.gray(' Or paste an existing token:'));
|
|
1433
|
+
console.log(chalk.white(' natureco infer auth paste-token'));
|
|
1434
|
+
console.log();
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
async function inferAuthPasteToken() {
|
|
1438
|
+
console.log(chalk.cyan('\n Paste Auth Token\n'));
|
|
1439
|
+
console.log(chalk.gray(' Paste your token below:'));
|
|
1440
|
+
console.log(chalk.yellow(' \u26A0 Interactive input not yet supported'));
|
|
1441
|
+
console.log(chalk.gray(' Use: natureco infer auth add <provider> <token>\n'));
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
async function inferAuthOrderGet() {
|
|
1445
|
+
const state = readState();
|
|
1446
|
+
const order = state.authOrder || ['openai', 'anthropic', 'groq', 'deepseek', 'mistral', 'xai', 'perplexity', 'together'];
|
|
1447
|
+
console.log(chalk.cyan('\n Auth Provider Order\n'));
|
|
1448
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(48)));
|
|
1449
|
+
order.forEach((p, i) => {
|
|
1450
|
+
console.log(' ' + chalk.white((i + 1) + '.') + ' ' + chalk.cyan(p));
|
|
1451
|
+
});
|
|
1452
|
+
console.log(chalk.gray('\n Set: ') + chalk.white('natureco infer auth order set <providers...>'));
|
|
1453
|
+
console.log();
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
async function inferAuthOrderSet(providers) {
|
|
1457
|
+
const state = readState();
|
|
1458
|
+
state.authOrder = providers;
|
|
1459
|
+
writeState(state);
|
|
1460
|
+
console.log(chalk.green('\n \u2713 Auth order set\n'));
|
|
1461
|
+
providers.forEach((p, i) => {
|
|
1462
|
+
console.log(' ' + chalk.white((i + 1) + '.') + ' ' + chalk.cyan(p));
|
|
1463
|
+
});
|
|
1464
|
+
console.log();
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
async function inferAuthOrderClear() {
|
|
1468
|
+
const state = readState();
|
|
1469
|
+
delete state.authOrder;
|
|
1470
|
+
writeState(state);
|
|
1471
|
+
console.log(chalk.green('\n \u2713 Auth order reset to default\n'));
|
|
1472
|
+
}
|
|
1473
|
+
|
|
73
1474
|
module.exports = infer;
|