Fix MITM on window
This commit is contained in:
@@ -21,7 +21,7 @@ const checkClaudeInstalled = async () => {
|
||||
try {
|
||||
const isWindows = os.platform() === "win32";
|
||||
const command = isWindows ? "where claude" : "command -v claude";
|
||||
await execAsync(command);
|
||||
await execAsync(command, { windowsHide: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
|
||||
@@ -76,7 +76,7 @@ const checkCodexInstalled = async () => {
|
||||
try {
|
||||
const isWindows = os.platform() === "win32";
|
||||
const command = isWindows ? "where codex" : "command -v codex";
|
||||
await execAsync(command);
|
||||
await execAsync(command, { windowsHide: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
|
||||
@@ -17,7 +17,7 @@ const checkDroidInstalled = async () => {
|
||||
try {
|
||||
const isWindows = os.platform() === "win32";
|
||||
const command = isWindows ? "where droid" : "command -v droid";
|
||||
await execAsync(command);
|
||||
await execAsync(command, { windowsHide: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
|
||||
@@ -17,7 +17,7 @@ const checkOpenClawInstalled = async () => {
|
||||
try {
|
||||
const isWindows = os.platform() === "win32";
|
||||
const command = isWindows ? "where openclaw" : "command -v openclaw";
|
||||
await execAsync(command);
|
||||
await execAsync(command, { windowsHide: true });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
|
||||
49
src/app/api/models/test/route.js
Normal file
49
src/app/api/models/test/route.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getApiKeys } from "@/lib/localDb";
|
||||
|
||||
// POST /api/models/test - Ping a single model via internal completions
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const { model } = await request.json();
|
||||
if (!model) return NextResponse.json({ error: "Model required" }, { status: 400 });
|
||||
|
||||
const url = new URL(request.url);
|
||||
const baseUrl = `${url.protocol}//${url.host}`;
|
||||
|
||||
// Get an active internal API key for auth (if requireApiKey is enabled)
|
||||
let apiKey = null;
|
||||
try {
|
||||
const keys = await getApiKeys();
|
||||
apiKey = keys.find((k) => k.isActive !== false)?.key || null;
|
||||
} catch {}
|
||||
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
||||
|
||||
const start = Date.now();
|
||||
const res = await fetch(`${baseUrl}/api/v1/chat/completions`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
model,
|
||||
max_tokens: 1,
|
||||
stream: false,
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
const latencyMs = Date.now() - start;
|
||||
|
||||
// 200 = ok; 400 = bad request but auth passed (model reachable)
|
||||
const ok = res.status === 200 || res.status === 400;
|
||||
let error = null;
|
||||
if (!ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
error = `HTTP ${res.status}${text ? `: ${text.slice(0, 120)}` : ""}`;
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok, latencyMs, error });
|
||||
} catch (err) {
|
||||
return NextResponse.json({ ok: false, error: err.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getProviderConnections, createProviderConnection, getProviderNodeById } from "@/models";
|
||||
import { getProviderConnections, createProviderConnection, getProviderNodeById, getProviderNodes } from "@/models";
|
||||
import { APIKEY_PROVIDERS } from "@/shared/constants/config";
|
||||
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
|
||||
|
||||
@@ -7,15 +7,31 @@ import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/sha
|
||||
export async function GET() {
|
||||
try {
|
||||
const connections = await getProviderConnections();
|
||||
|
||||
// Build nodeNameMap for compatible providers (id → name)
|
||||
let nodeNameMap = {};
|
||||
try {
|
||||
const nodes = await getProviderNodes();
|
||||
for (const node of nodes) {
|
||||
if (node.id && node.name) nodeNameMap[node.id] = node.name;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Hide sensitive fields
|
||||
const safeConnections = connections.map(c => ({
|
||||
...c,
|
||||
apiKey: undefined,
|
||||
accessToken: undefined,
|
||||
refreshToken: undefined,
|
||||
idToken: undefined,
|
||||
}));
|
||||
// Hide sensitive fields, enrich name for compatible providers
|
||||
const safeConnections = connections.map(c => {
|
||||
const isCompatible = isOpenAICompatibleProvider(c.provider) || isAnthropicCompatibleProvider(c.provider);
|
||||
const name = isCompatible
|
||||
? (nodeNameMap[c.provider] || c.providerSpecificData?.nodeName || c.provider)
|
||||
: c.name;
|
||||
return {
|
||||
...c,
|
||||
name,
|
||||
apiKey: undefined,
|
||||
accessToken: undefined,
|
||||
refreshToken: undefined,
|
||||
idToken: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({ connections: safeConnections });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user