infernoflow 0.32.7 → 0.32.9
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/dist/bin/infernoflow.mjs +84 -255
- package/dist/lib/adopters/angular.mjs +1 -128
- package/dist/lib/adopters/css.mjs +1 -111
- package/dist/lib/adopters/react.mjs +1 -104
- package/dist/lib/ai/ideDetection.mjs +1 -31
- package/dist/lib/ai/localProvider.mjs +1 -88
- package/dist/lib/ai/providerRouter.mjs +2 -295
- package/dist/lib/commands/adopt.mjs +20 -869
- package/dist/lib/commands/adoptWizard.mjs +9 -320
- package/dist/lib/commands/agent.mjs +5 -191
- package/dist/lib/commands/ai.mjs +2 -407
- package/dist/lib/commands/audit.mjs +13 -300
- package/dist/lib/commands/changelog.mjs +26 -594
- package/dist/lib/commands/check.mjs +3 -184
- package/dist/lib/commands/ci.mjs +3 -208
- package/dist/lib/commands/claudeMd.mjs +25 -130
- package/dist/lib/commands/cloud.mjs +5 -521
- package/dist/lib/commands/context.mjs +31 -287
- package/dist/lib/commands/coverage.mjs +2 -282
- package/dist/lib/commands/dashboard.mjs +123 -635
- package/dist/lib/commands/demo.mjs +8 -465
- package/dist/lib/commands/diff.mjs +5 -274
- package/dist/lib/commands/docGate.mjs +2 -81
- package/dist/lib/commands/doctor.mjs +3 -321
- package/dist/lib/commands/explain.mjs +8 -438
- package/dist/lib/commands/export.mjs +10 -239
- package/dist/lib/commands/generateSkills.mjs +38 -163
- package/dist/lib/commands/graph.mjs +203 -320
- package/dist/lib/commands/health.mjs +2 -309
- package/dist/lib/commands/impact.mjs +2 -325
- package/dist/lib/commands/implement.mjs +7 -103
- package/dist/lib/commands/init.mjs +23 -475
- package/dist/lib/commands/installCursorHooks.mjs +1 -36
- package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
- package/dist/lib/commands/link.mjs +2 -342
- package/dist/lib/commands/monorepo.mjs +4 -428
- package/dist/lib/commands/notify.mjs +4 -258
- package/dist/lib/commands/onboard.mjs +4 -296
- package/dist/lib/commands/prComment.mjs +2 -361
- package/dist/lib/commands/prImpact.mjs +2 -157
- package/dist/lib/commands/publish.mjs +15 -316
- package/dist/lib/commands/report.mjs +28 -272
- package/dist/lib/commands/review.mjs +9 -223
- package/dist/lib/commands/run.mjs +8 -336
- package/dist/lib/commands/scaffold.mjs +54 -419
- package/dist/lib/commands/scan.mjs +5 -558
- package/dist/lib/commands/scout.mjs +2 -291
- package/dist/lib/commands/setup.mjs +5 -310
- package/dist/lib/commands/share.mjs +13 -196
- package/dist/lib/commands/snapshot.mjs +3 -383
- package/dist/lib/commands/stability.mjs +2 -293
- package/dist/lib/commands/status.mjs +4 -172
- package/dist/lib/commands/suggest.mjs +21 -563
- package/dist/lib/commands/syncAuto.mjs +1 -96
- package/dist/lib/commands/synthesize.mjs +10 -228
- package/dist/lib/commands/teamSync.mjs +2 -388
- package/dist/lib/commands/test.mjs +6 -363
- package/dist/lib/commands/version.mjs +2 -282
- package/dist/lib/commands/vibe.mjs +7 -357
- package/dist/lib/commands/watch.mjs +4 -203
- package/dist/lib/commands/why.mjs +4 -358
- package/dist/lib/cursorHooksInstall.mjs +1 -60
- package/dist/lib/draftToolingInstall.mjs +7 -68
- package/dist/lib/git/detect-drift.mjs +4 -208
- package/dist/lib/learning/adapt.mjs +6 -101
- package/dist/lib/learning/observe.mjs +1 -119
- package/dist/lib/learning/patternDetector.mjs +1 -298
- package/dist/lib/learning/profile.mjs +2 -279
- package/dist/lib/learning/skillSynthesizer.mjs +24 -145
- package/dist/lib/templates/index.mjs +1 -131
- package/dist/lib/ui/errors.mjs +1 -142
- package/dist/lib/ui/output.mjs +6 -72
- package/dist/lib/ui/prompts.mjs +6 -147
- package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
- package/dist/templates/cursor/inferno-mcp-server.mjs +29 -0
- package/dist/templates/github-app/GITHUB_APP.md +67 -0
- package/dist/templates/github-app/app-manifest.json +20 -0
- package/package.json +1 -1
|
@@ -1,295 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Tries providers in order until one works:
|
|
5
|
-
* Tier 1 — VS Code Language Model API (vscode.lm — any Copilot model: Gemini, Claude, GPT)
|
|
6
|
-
* Tier 2 — Direct API: Anthropic, OpenAI, Google AI (Gemini), OpenRouter
|
|
7
|
-
* Tier 3 — Ollama (local, free, offline)
|
|
8
|
-
* Tier 4 — Prompt fallback (print prompt, no AI call)
|
|
9
|
-
*
|
|
10
|
-
* Config sources (in priority order):
|
|
11
|
-
* 1. Environment variables
|
|
12
|
-
* 2. inferno/integrations.json
|
|
13
|
-
* 3. Auto-detection (Ollama running locally, etc.)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from "node:fs";
|
|
17
|
-
import * as path from "node:path";
|
|
18
|
-
import * as https from "node:https";
|
|
19
|
-
import * as http from "node:http";
|
|
20
|
-
|
|
21
|
-
// ── Config reader ─────────────────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
export function readAiConfig(cwd) {
|
|
24
|
-
const p = path.join(cwd, "inferno", "integrations.json");
|
|
25
|
-
if (!fs.existsSync(p)) return {};
|
|
26
|
-
try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return {}; }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
function post(url, headers, body) {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
const parsed = new URL(url);
|
|
34
|
-
const lib = parsed.protocol === "https:" ? https : http;
|
|
35
|
-
const data = JSON.stringify(body);
|
|
36
|
-
|
|
37
|
-
const req = lib.request({
|
|
38
|
-
hostname: parsed.hostname,
|
|
39
|
-
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
40
|
-
path: parsed.pathname + (parsed.search || ""),
|
|
41
|
-
method: "POST",
|
|
42
|
-
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(data), ...headers },
|
|
43
|
-
}, (res) => {
|
|
44
|
-
let raw = "";
|
|
45
|
-
res.on("data", d => (raw += d));
|
|
46
|
-
res.on("end", () => {
|
|
47
|
-
try { resolve({ status: res.statusCode, body: JSON.parse(raw) }); }
|
|
48
|
-
catch { resolve({ status: res.statusCode, body: raw }); }
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
req.on("error", reject);
|
|
52
|
-
req.write(data);
|
|
53
|
-
req.end();
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ── Tier 2: Direct API providers ─────────────────────────────────────────────
|
|
58
|
-
|
|
59
|
-
async function callAnthropic(prompt, config) {
|
|
60
|
-
const apiKey = process.env.ANTHROPIC_API_KEY || config.anthropic?.apiKey;
|
|
61
|
-
if (!apiKey) return null;
|
|
62
|
-
|
|
63
|
-
const model = config.anthropic?.model || process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6";
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const res = await post(
|
|
67
|
-
"https://api.anthropic.com/v1/messages",
|
|
68
|
-
{ "x-api-key": apiKey, "anthropic-version": "2023-06-01" },
|
|
69
|
-
{
|
|
70
|
-
model,
|
|
71
|
-
max_tokens: 1024,
|
|
72
|
-
messages: [{ role: "user", content: prompt }],
|
|
73
|
-
}
|
|
74
|
-
);
|
|
75
|
-
if (res.status === 200 && res.body?.content?.[0]?.text) {
|
|
76
|
-
return { text: res.body.content[0].text, provider: "anthropic", model };
|
|
77
|
-
}
|
|
78
|
-
} catch {}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function callOpenAI(prompt, config) {
|
|
83
|
-
const apiKey = process.env.OPENAI_API_KEY || config.openai?.apiKey;
|
|
84
|
-
const endpoint = process.env.OPENAI_ENDPOINT || config.openai?.endpoint || "https://api.openai.com/v1/chat/completions";
|
|
85
|
-
if (!apiKey) return null;
|
|
86
|
-
|
|
87
|
-
const model = config.openai?.model || process.env.OPENAI_MODEL || "gpt-4o";
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const res = await post(
|
|
91
|
-
endpoint,
|
|
92
|
-
{ "Authorization": `Bearer ${apiKey}` },
|
|
93
|
-
{
|
|
94
|
-
model,
|
|
95
|
-
max_tokens: 1024,
|
|
96
|
-
messages: [{ role: "user", content: prompt }],
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
if (res.status === 200 && res.body?.choices?.[0]?.message?.content) {
|
|
100
|
-
return { text: res.body.choices[0].message.content, provider: "openai", model };
|
|
101
|
-
}
|
|
102
|
-
} catch {}
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function callGemini(prompt, config) {
|
|
107
|
-
const apiKey = process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || config.gemini?.apiKey;
|
|
108
|
-
if (!apiKey) return null;
|
|
109
|
-
|
|
110
|
-
const model = config.gemini?.model || process.env.GEMINI_MODEL || "gemini-2.0-flash";
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
const res = await post(
|
|
114
|
-
`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`,
|
|
115
|
-
{},
|
|
116
|
-
{ contents: [{ parts: [{ text: prompt }] }] }
|
|
117
|
-
);
|
|
118
|
-
const text = res.body?.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
119
|
-
if (res.status === 200 && text) {
|
|
120
|
-
return { text, provider: "gemini", model };
|
|
121
|
-
}
|
|
122
|
-
} catch {}
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function callOpenRouter(prompt, config) {
|
|
127
|
-
const apiKey = process.env.OPENROUTER_API_KEY || config.openrouter?.apiKey;
|
|
128
|
-
if (!apiKey) return null;
|
|
129
|
-
|
|
130
|
-
const model = config.openrouter?.model || process.env.OPENROUTER_MODEL || "anthropic/claude-sonnet-4-6";
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
const res = await post(
|
|
134
|
-
"https://openrouter.ai/api/v1/chat/completions",
|
|
135
|
-
{ "Authorization": `Bearer ${apiKey}`, "HTTP-Referer": "https://infernoflow.dev" },
|
|
136
|
-
{
|
|
137
|
-
model,
|
|
138
|
-
messages: [{ role: "user", content: prompt }],
|
|
139
|
-
max_tokens: 1024,
|
|
140
|
-
}
|
|
141
|
-
);
|
|
142
|
-
if (res.status === 200 && res.body?.choices?.[0]?.message?.content) {
|
|
143
|
-
return { text: res.body.choices[0].message.content, provider: "openrouter", model };
|
|
144
|
-
}
|
|
145
|
-
} catch {}
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ── Tier 3: Ollama (local) ────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
async function callOllama(prompt, config) {
|
|
152
|
-
const host = process.env.OLLAMA_HOST || config.ollama?.host || "http://localhost:11434";
|
|
153
|
-
const model = process.env.OLLAMA_MODEL || config.ollama?.model || "llama3";
|
|
154
|
-
|
|
155
|
-
// Quick liveness check
|
|
156
|
-
try {
|
|
157
|
-
await new Promise((res, rej) => {
|
|
158
|
-
const u = new URL(host);
|
|
159
|
-
http.get({ hostname: u.hostname, port: u.port || 11434, path: "/api/tags", timeout: 1500 },
|
|
160
|
-
r => res(r)).on("error", rej);
|
|
161
|
-
});
|
|
162
|
-
} catch { return null; }
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
const res = await post(
|
|
166
|
-
`${host}/api/generate`,
|
|
167
|
-
{},
|
|
168
|
-
{ model, prompt, stream: false }
|
|
169
|
-
);
|
|
170
|
-
if (res.status === 200 && res.body?.response) {
|
|
171
|
-
return { text: res.body.response, provider: "ollama", model };
|
|
172
|
-
}
|
|
173
|
-
} catch {}
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// ── Main router ───────────────────────────────────────────────────────────────
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Call the best available AI provider with a prompt.
|
|
181
|
-
*
|
|
182
|
-
* @param {string} prompt - The full prompt text
|
|
183
|
-
* @param {object} opts
|
|
184
|
-
* opts.cwd - Project root (for reading integrations.json)
|
|
185
|
-
* opts.provider - Force a specific provider: anthropic|openai|gemini|openrouter|ollama|prompt
|
|
186
|
-
* opts.silent - Don't print "using provider X" message
|
|
187
|
-
* @returns {{ text: string, provider: string, model: string } | null}
|
|
188
|
-
* null means no provider was available → caller should use prompt fallback
|
|
189
|
-
*/
|
|
190
|
-
export async function callAI(prompt, opts = {}) {
|
|
191
|
-
const cwd = opts.cwd || process.cwd();
|
|
192
|
-
const config = readAiConfig(cwd);
|
|
193
|
-
const forced = (opts.provider || "auto").toLowerCase();
|
|
194
|
-
const silent = opts.silent ?? true;
|
|
195
|
-
|
|
196
|
-
const providers = [
|
|
197
|
-
// Tier 2 — direct API (Tier 1 vscode.lm is handled in the VS Code extension)
|
|
198
|
-
["anthropic", () => callAnthropic(prompt, config)],
|
|
199
|
-
["openai", () => callOpenAI(prompt, config)],
|
|
200
|
-
["gemini", () => callGemini(prompt, config)],
|
|
201
|
-
["openrouter", () => callOpenRouter(prompt, config)],
|
|
202
|
-
// Tier 3 — local
|
|
203
|
-
["ollama", () => callOllama(prompt, config)],
|
|
204
|
-
];
|
|
205
|
-
|
|
206
|
-
// If a specific provider is forced, only try that one
|
|
207
|
-
const toTry = forced === "auto" || forced === "prompt"
|
|
208
|
-
? providers
|
|
209
|
-
: providers.filter(([name]) => name === forced);
|
|
210
|
-
|
|
211
|
-
for (const [name, fn] of toTry) {
|
|
212
|
-
try {
|
|
213
|
-
const result = await fn();
|
|
214
|
-
if (result) {
|
|
215
|
-
if (!silent) process.stderr.write(` [infernoflow ai] using ${name}:${result.model}\n`);
|
|
216
|
-
return result;
|
|
217
|
-
}
|
|
218
|
-
} catch {}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return null; // No provider → fallback to prompt output
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Detect which providers are configured (for doctor command).
|
|
226
|
-
*/
|
|
227
|
-
export function detectAvailableProviders(cwd) {
|
|
228
|
-
const config = readAiConfig(cwd);
|
|
229
|
-
return {
|
|
230
|
-
anthropic: !!(process.env.ANTHROPIC_API_KEY || config.anthropic?.apiKey),
|
|
231
|
-
openai: !!(process.env.OPENAI_API_KEY || config.openai?.apiKey),
|
|
232
|
-
gemini: !!(process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || config.gemini?.apiKey),
|
|
233
|
-
openrouter: !!(process.env.OPENROUTER_API_KEY || config.openrouter?.apiKey),
|
|
234
|
-
ollama: false, // checked async — doctor runs its own check
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Resolve which provider + IDE is available for the `run` command.
|
|
240
|
-
* Returns a structured object that run.mjs uses to decide how to proceed.
|
|
241
|
-
*
|
|
242
|
-
* @param {string} providerRequested - "auto"|"anthropic"|"openai"|etc.
|
|
243
|
-
* @param {string} ideRequested - "auto"|"vscode"|"cursor"|etc.
|
|
244
|
-
* @returns {{ providerResolved: string, ideDetected: string, agentAvailable: boolean, reasonCodes: string[], error?: string }}
|
|
245
|
-
*/
|
|
246
|
-
export async function resolveProvider(providerRequested = "auto", ideRequested = "auto") {
|
|
247
|
-
const cwd = process.cwd();
|
|
248
|
-
const config = readAiConfig(cwd);
|
|
249
|
-
const reasons = [];
|
|
250
|
-
|
|
251
|
-
// Detect IDE
|
|
252
|
-
const inVsCode = !!process.env.VSCODE_PID || !!process.env.TERM_PROGRAM?.includes("vscode");
|
|
253
|
-
const inCursor = !!process.env.CURSOR_TRACE_ID || !!process.env.CURSOR_CHANNEL;
|
|
254
|
-
const ideDetected = inCursor ? "cursor" : inVsCode ? "vscode" : "terminal";
|
|
255
|
-
|
|
256
|
-
// Detect available providers
|
|
257
|
-
const available = {
|
|
258
|
-
anthropic: !!(process.env.ANTHROPIC_API_KEY || config.anthropic?.apiKey),
|
|
259
|
-
openai: !!(process.env.OPENAI_API_KEY || config.openai?.apiKey),
|
|
260
|
-
gemini: !!(process.env.GOOGLE_AI_API_KEY || process.env.GEMINI_API_KEY || config.gemini?.apiKey),
|
|
261
|
-
openrouter: !!(process.env.OPENROUTER_API_KEY || config.openrouter?.apiKey),
|
|
262
|
-
ollama: false,
|
|
263
|
-
vscode: inVsCode || inCursor,
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// Check Ollama quickly (sync port probe via env hint)
|
|
267
|
-
if (process.env.OLLAMA_HOST || config.ollama?.host) {
|
|
268
|
-
available.ollama = true;
|
|
269
|
-
reasons.push("ollama_env");
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Resolve which provider to use
|
|
273
|
-
let providerResolved = "none";
|
|
274
|
-
const forced = (providerRequested || "auto").toLowerCase();
|
|
275
|
-
|
|
276
|
-
if (forced !== "auto" && forced !== "prompt" && available[forced]) {
|
|
277
|
-
providerResolved = forced;
|
|
278
|
-
reasons.push(`forced_${forced}`);
|
|
279
|
-
} else {
|
|
280
|
-
// Priority order: vscode/cursor IDE → anthropic → openai → gemini → openrouter → ollama
|
|
281
|
-
const priority = ["vscode", "anthropic", "openai", "gemini", "openrouter", "ollama"];
|
|
282
|
-
for (const p of priority) {
|
|
283
|
-
if (available[p]) { providerResolved = p; reasons.push(`auto_${p}`); break; }
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const agentAvailable = providerResolved !== "none";
|
|
288
|
-
|
|
289
|
-
if (!agentAvailable) {
|
|
290
|
-
reasons.push("no_provider");
|
|
291
|
-
return { providerResolved: "none", ideDetected, agentAvailable: false, reasonCodes: reasons, error: "agent_unavailable" };
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return { providerResolved, ideDetected, agentAvailable: true, reasonCodes: reasons };
|
|
295
|
-
}
|
|
1
|
+
import*as v from"node:fs";import*as y from"node:path";import*as A from"node:https";import*as _ from"node:http";function m(n){const t=y.join(n,"inferno","integrations.json");if(!v.existsSync(t))return{};try{return JSON.parse(v.readFileSync(t,"utf8"))}catch{return{}}}function u(n,t,s){return new Promise((o,e)=>{const r=new URL(n),p=r.protocol==="https:"?A:_,l=JSON.stringify(s),c=p.request({hostname:r.hostname,port:r.port||(r.protocol==="https:"?443:80),path:r.pathname+(r.search||""),method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(l),...t}},i=>{let a="";i.on("data",h=>a+=h),i.on("end",()=>{try{o({status:i.statusCode,body:JSON.parse(a)})}catch{o({status:i.statusCode,body:a})}})});c.on("error",e),c.write(l),c.end()})}async function E(n,t){const s=process.env.ANTHROPIC_API_KEY||t.anthropic?.apiKey;if(!s)return null;const o=t.anthropic?.model||process.env.ANTHROPIC_MODEL||"claude-sonnet-4-6";try{const e=await u("https://api.anthropic.com/v1/messages",{"x-api-key":s,"anthropic-version":"2023-06-01"},{model:o,max_tokens:1024,messages:[{role:"user",content:n}]});if(e.status===200&&e.body?.content?.[0]?.text)return{text:e.body.content[0].text,provider:"anthropic",model:o}}catch{}return null}async function O(n,t){const s=process.env.OPENAI_API_KEY||t.openai?.apiKey,o=process.env.OPENAI_ENDPOINT||t.openai?.endpoint||"https://api.openai.com/v1/chat/completions";if(!s)return null;const e=t.openai?.model||process.env.OPENAI_MODEL||"gpt-4o";try{const r=await u(o,{Authorization:`Bearer ${s}`},{model:e,max_tokens:1024,messages:[{role:"user",content:n}]});if(r.status===200&&r.body?.choices?.[0]?.message?.content)return{text:r.body.choices[0].message.content,provider:"openai",model:e}}catch{}return null}async function I(n,t){const s=process.env.GOOGLE_AI_API_KEY||process.env.GEMINI_API_KEY||t.gemini?.apiKey;if(!s)return null;const o=t.gemini?.model||process.env.GEMINI_MODEL||"gemini-2.0-flash";try{const e=await u(`https://generativelanguage.googleapis.com/v1beta/models/${o}:generateContent?key=${s}`,{},{contents:[{parts:[{text:n}]}]}),r=e.body?.candidates?.[0]?.content?.parts?.[0]?.text;if(e.status===200&&r)return{text:r,provider:"gemini",model:o}}catch{}return null}async function P(n,t){const s=process.env.OPENROUTER_API_KEY||t.openrouter?.apiKey;if(!s)return null;const o=t.openrouter?.model||process.env.OPENROUTER_MODEL||"anthropic/claude-sonnet-4-6";try{const e=await u("https://openrouter.ai/api/v1/chat/completions",{Authorization:`Bearer ${s}`,"HTTP-Referer":"https://infernoflow.dev"},{model:o,messages:[{role:"user",content:n}],max_tokens:1024});if(e.status===200&&e.body?.choices?.[0]?.message?.content)return{text:e.body.choices[0].message.content,provider:"openrouter",model:o}}catch{}return null}async function g(n,t){const s=process.env.OLLAMA_HOST||t.ollama?.host||"http://localhost:11434",o=process.env.OLLAMA_MODEL||t.ollama?.model||"llama3";try{await new Promise((e,r)=>{const p=new URL(s);_.get({hostname:p.hostname,port:p.port||11434,path:"/api/tags",timeout:1500},l=>e(l)).on("error",r)})}catch{return null}try{const e=await u(`${s}/api/generate`,{},{model:o,prompt:n,stream:!1});if(e.status===200&&e.body?.response)return{text:e.body.response,provider:"ollama",model:o}}catch{}return null}async function K(n,t={}){const s=t.cwd||process.cwd(),o=m(s),e=(t.provider||"auto").toLowerCase(),r=t.silent??!0,p=[["anthropic",()=>E(n,o)],["openai",()=>O(n,o)],["gemini",()=>I(n,o)],["openrouter",()=>P(n,o)],["ollama",()=>g(n,o)]],l=e==="auto"||e==="prompt"?p:p.filter(([c])=>c===e);for(const[c,i]of l)try{const a=await i();if(a)return r||process.stderr.write(` [infernoflow ai] using ${c}:${a.model}
|
|
2
|
+
`),a}catch{}return null}function R(n){const t=m(n);return{anthropic:!!(process.env.ANTHROPIC_API_KEY||t.anthropic?.apiKey),openai:!!(process.env.OPENAI_API_KEY||t.openai?.apiKey),gemini:!!(process.env.GOOGLE_AI_API_KEY||process.env.GEMINI_API_KEY||t.gemini?.apiKey),openrouter:!!(process.env.OPENROUTER_API_KEY||t.openrouter?.apiKey),ollama:!1}}async function N(n="auto",t="auto"){const s=process.cwd(),o=m(s),e=[],r=!!process.env.VSCODE_PID||!!process.env.TERM_PROGRAM?.includes("vscode"),p=!!process.env.CURSOR_TRACE_ID||!!process.env.CURSOR_CHANNEL,l=p?"cursor":r?"vscode":"terminal",c={anthropic:!!(process.env.ANTHROPIC_API_KEY||o.anthropic?.apiKey),openai:!!(process.env.OPENAI_API_KEY||o.openai?.apiKey),gemini:!!(process.env.GOOGLE_AI_API_KEY||process.env.GEMINI_API_KEY||o.gemini?.apiKey),openrouter:!!(process.env.OPENROUTER_API_KEY||o.openrouter?.apiKey),ollama:!1,vscode:r||p};(process.env.OLLAMA_HOST||o.ollama?.host)&&(c.ollama=!0,e.push("ollama_env"));let i="none";const a=(n||"auto").toLowerCase();if(a!=="auto"&&a!=="prompt"&&c[a])i=a,e.push(`forced_${a}`);else{const f=["vscode","anthropic","openai","gemini","openrouter","ollama"];for(const d of f)if(c[d]){i=d,e.push(`auto_${d}`);break}}return i!=="none"?{providerResolved:i,ideDetected:l,agentAvailable:!0,reasonCodes:e}:(e.push("no_provider"),{providerResolved:"none",ideDetected:l,agentAvailable:!1,reasonCodes:e,error:"agent_unavailable"})}export{K as callAI,R as detectAvailableProviders,m as readAiConfig,N as resolveProvider};
|