@almightygpt/core 0.10.1 → 0.11.1
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/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +5 -0
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/defaults.d.ts.map +1 -1
- package/dist/adapters/defaults.js +5 -0
- package/dist/adapters/defaults.js.map +1 -1
- package/dist/adapters/factory.d.ts.map +1 -1
- package/dist/adapters/factory.js +11 -1
- package/dist/adapters/factory.js.map +1 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +5 -0
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/index.d.ts +9 -5
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +9 -5
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/ollama.d.ts +47 -0
- package/dist/adapters/ollama.d.ts.map +1 -0
- package/dist/adapters/ollama.js +124 -0
- package/dist/adapters/ollama.js.map +1 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +8 -0
- package/dist/adapters/openai.js.map +1 -1
- package/dist/adapters/openrouter.d.ts +50 -0
- package/dist/adapters/openrouter.d.ts.map +1 -0
- package/dist/adapters/openrouter.js +153 -0
- package/dist/adapters/openrouter.js.map +1 -0
- package/dist/auth/__tests__/resolver.test.js +18 -0
- package/dist/auth/__tests__/resolver.test.js.map +1 -1
- package/dist/auth/types.d.ts +1 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/auth/types.js +8 -0
- package/dist/auth/types.js.map +1 -1
- package/dist/auth/validator.d.ts.map +1 -1
- package/dist/auth/validator.js +127 -0
- package/dist/auth/validator.js.map +1 -1
- package/dist/config/schema.d.ts +12 -12
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +8 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/adapters/claude.ts +8 -0
- package/src/adapters/defaults.ts +5 -0
- package/src/adapters/factory.ts +11 -1
- package/src/adapters/gemini.ts +8 -0
- package/src/adapters/index.ts +9 -5
- package/src/adapters/ollama.ts +157 -0
- package/src/adapters/openai.ts +11 -0
- package/src/adapters/openrouter.ts +202 -0
- package/src/auth/__tests__/resolver.test.ts +24 -0
- package/src/auth/types.ts +15 -1
- package/src/auth/validator.ts +135 -0
- package/src/config/schema.ts +8 -1
- package/src/index.ts +1 -1
package/src/auth/validator.ts
CHANGED
|
@@ -72,6 +72,20 @@ export async function validateKey(
|
|
|
72
72
|
case "google":
|
|
73
73
|
result = await validateGoogle(key);
|
|
74
74
|
break;
|
|
75
|
+
case "openrouter":
|
|
76
|
+
result = await validateOpenRouter(key);
|
|
77
|
+
break;
|
|
78
|
+
case "ollama":
|
|
79
|
+
// Ollama has no key — "validation" means probing the local
|
|
80
|
+
// daemon. The key arg is ignored.
|
|
81
|
+
result = await validateOllama();
|
|
82
|
+
break;
|
|
83
|
+
default: {
|
|
84
|
+
// Exhaustiveness check — TypeScript will error here if a new
|
|
85
|
+
// ProviderId is added without a switch case.
|
|
86
|
+
const _exhaustive: never = provider;
|
|
87
|
+
throw new Error(`Unknown provider: ${String(_exhaustive)}`);
|
|
88
|
+
}
|
|
75
89
|
}
|
|
76
90
|
result.latencyMs = Date.now() - start;
|
|
77
91
|
return result;
|
|
@@ -187,6 +201,105 @@ async function validateGoogle(key: string): Promise<ValidationResult> {
|
|
|
187
201
|
}
|
|
188
202
|
}
|
|
189
203
|
|
|
204
|
+
async function validateOpenRouter(key: string): Promise<ValidationResult> {
|
|
205
|
+
// OpenRouter is OpenAI-compatible at https://openrouter.ai/api/v1.
|
|
206
|
+
// Same chat-completions shape — point fetch at OR's base URL.
|
|
207
|
+
const model = DEFAULT_MODELS.openrouter;
|
|
208
|
+
const controller = new AbortController();
|
|
209
|
+
const timer = setTimeout(() => controller.abort(), VALIDATION_TIMEOUT_MS);
|
|
210
|
+
try {
|
|
211
|
+
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
212
|
+
method: "POST",
|
|
213
|
+
headers: {
|
|
214
|
+
"content-type": "application/json",
|
|
215
|
+
authorization: `Bearer ${key}`,
|
|
216
|
+
"HTTP-Referer": "https://github.com/roxjayanath/almightygpt",
|
|
217
|
+
"X-Title": "AlmightyGPT validation",
|
|
218
|
+
},
|
|
219
|
+
body: JSON.stringify({
|
|
220
|
+
model,
|
|
221
|
+
messages: [{ role: "user", content: "hi" }],
|
|
222
|
+
max_tokens: 1,
|
|
223
|
+
}),
|
|
224
|
+
signal: controller.signal,
|
|
225
|
+
});
|
|
226
|
+
if (!res.ok) {
|
|
227
|
+
const rawBody = await res.text().catch(() => "");
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
statusCode: res.status,
|
|
231
|
+
error: normalizeOpenRouterError(res.status, rawBody, key),
|
|
232
|
+
rawBody,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const data = (await res.json()) as { model?: string };
|
|
236
|
+
return { ok: true, model: data.model ?? model };
|
|
237
|
+
} finally {
|
|
238
|
+
clearTimeout(timer);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function validateOllama(): Promise<ValidationResult> {
|
|
243
|
+
// v0.12.1 (per Codex P2 #3): probe /api/tags AND check the
|
|
244
|
+
// configured default model is installed. Previously this only
|
|
245
|
+
// verified the daemon was running — users would see "valid" then
|
|
246
|
+
// their first review would fail because llama3.3:70b was never
|
|
247
|
+
// pulled. For a local-first provider, that's the wrong first
|
|
248
|
+
// impression.
|
|
249
|
+
const baseUrl = process.env["OLLAMA_BASE_URL"] ?? "http://localhost:11434";
|
|
250
|
+
const probeUrl = baseUrl.replace(/\/v1\/?$/, "") + "/api/tags";
|
|
251
|
+
const expectedModel = DEFAULT_MODELS.ollama;
|
|
252
|
+
const controller = new AbortController();
|
|
253
|
+
const timer = setTimeout(() => controller.abort(), 3000);
|
|
254
|
+
try {
|
|
255
|
+
const res = await fetch(probeUrl, { signal: controller.signal });
|
|
256
|
+
if (!res.ok) {
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
statusCode: res.status,
|
|
260
|
+
error: `[${res.status}] Ollama daemon at ${baseUrl} responded with an error.`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// Parse the model list — shape is { models: [{ name: string, ... }] }.
|
|
264
|
+
const data = (await res.json()) as { models?: Array<{ name?: string }> };
|
|
265
|
+
const installed = (data.models ?? [])
|
|
266
|
+
.map((m) => m.name)
|
|
267
|
+
.filter((n): n is string => typeof n === "string");
|
|
268
|
+
|
|
269
|
+
// Match strategy: exact name match, OR base-name match (e.g. the
|
|
270
|
+
// default "llama3.3:70b" should also be considered installed if
|
|
271
|
+
// the user only has "llama3.3:latest" or another tag of the same
|
|
272
|
+
// base model — close enough for "did you pull this family?").
|
|
273
|
+
const expectedBase = expectedModel.split(":")[0] ?? expectedModel;
|
|
274
|
+
const hasExact = installed.includes(expectedModel);
|
|
275
|
+
const hasBase = installed.some((n) => n.split(":")[0] === expectedBase);
|
|
276
|
+
if (!hasExact && !hasBase) {
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
error:
|
|
280
|
+
`Ollama daemon at ${baseUrl} is running, but the configured ` +
|
|
281
|
+
`default model "${expectedModel}" is not installed locally. ` +
|
|
282
|
+
`Pull it first: ollama pull ${expectedModel}` +
|
|
283
|
+
(installed.length > 0
|
|
284
|
+
? `\n(Installed models: ${installed.slice(0, 5).join(", ")}${installed.length > 5 ? "…" : ""})`
|
|
285
|
+
: "\n(No models installed yet.)"),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return { ok: true, model: expectedModel };
|
|
290
|
+
} catch (err) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
error:
|
|
294
|
+
`Ollama daemon unreachable at ${baseUrl}. Is Ollama installed ` +
|
|
295
|
+
`and running? Try \`ollama serve\` or open the Ollama app.`,
|
|
296
|
+
rawBody: err instanceof Error ? err.message : String(err),
|
|
297
|
+
};
|
|
298
|
+
} finally {
|
|
299
|
+
clearTimeout(timer);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
190
303
|
// ─── Error normalization (Codex v0.8 P2 #6) ──────────────────────────
|
|
191
304
|
//
|
|
192
305
|
// Parse known provider JSON error shapes into short, user-safe messages.
|
|
@@ -258,6 +371,28 @@ function redactKey(msg: string, key: string): string {
|
|
|
258
371
|
return msg.split(key).join("<redacted-key>");
|
|
259
372
|
}
|
|
260
373
|
|
|
374
|
+
function normalizeOpenRouterError(
|
|
375
|
+
status: number,
|
|
376
|
+
rawBody: string,
|
|
377
|
+
submittedKey: string,
|
|
378
|
+
): string {
|
|
379
|
+
// OpenRouter forwards provider errors but wraps them; shape is
|
|
380
|
+
// typically { "error": { "message": "...", "code": N } }.
|
|
381
|
+
try {
|
|
382
|
+
const parsed = JSON.parse(rawBody) as {
|
|
383
|
+
error?: { message?: string; code?: number };
|
|
384
|
+
};
|
|
385
|
+
let msg = parsed.error?.message ?? "";
|
|
386
|
+
if (submittedKey && msg.includes(submittedKey)) {
|
|
387
|
+
msg = msg.replace(submittedKey, "<redacted-key>");
|
|
388
|
+
}
|
|
389
|
+
if (msg) return `[${status}] OpenRouter: ${truncate(msg, 200)}`;
|
|
390
|
+
} catch {
|
|
391
|
+
/* fall through */
|
|
392
|
+
}
|
|
393
|
+
return statusOnlyMessage("OpenRouter", status);
|
|
394
|
+
}
|
|
395
|
+
|
|
261
396
|
function statusOnlyMessage(provider: string, status: number): string {
|
|
262
397
|
if (status === 401 || status === 403) {
|
|
263
398
|
return `[${status}] ${provider} rejected the key (unauthorized).`;
|
package/src/config/schema.ts
CHANGED
|
@@ -13,7 +13,14 @@ export const AgentRoleSchema = z.enum(["worker", "reviewer", "both", "optional"]
|
|
|
13
13
|
export const AgentConfigSchema = z
|
|
14
14
|
.object({
|
|
15
15
|
enabled: z.boolean().default(true),
|
|
16
|
-
provider: z.enum([
|
|
16
|
+
provider: z.enum([
|
|
17
|
+
"openai",
|
|
18
|
+
"anthropic",
|
|
19
|
+
"google",
|
|
20
|
+
"openrouter",
|
|
21
|
+
"ollama",
|
|
22
|
+
"mock",
|
|
23
|
+
]),
|
|
17
24
|
mode: z.enum(["api", "cli"]).default("api"),
|
|
18
25
|
role: AgentRoleSchema.default("optional"),
|
|
19
26
|
memoryFile: z.string().min(1),
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - budget/ ✅ task #14 BudgetTracker + BudgetExceededError
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
export const VERSION = "0.
|
|
16
|
+
export const VERSION = "0.11.1";
|
|
17
17
|
|
|
18
18
|
// MCP server (v0.9.0+) — exposes AlmightyGPT's review surface as MCP tools.
|
|
19
19
|
export { startMcpServer } from "./mcp/server.js";
|