feat: add ignore-certificate-errors option for self-signed TLS

This commit is contained in:
2026-08-28 09:58:58 -06:00
parent f98043c21b
commit 973d5686fc
14 changed files with 149 additions and 8 deletions
+2 -1
View File
@@ -1,3 +1,4 @@
import { fetchWith } from "./http";
import { type LlamaSwapConfig } from "./util";
import { parseSse, type SseMessage } from "./sse";
import { type FeedEvent, type InflightRequest, type ModelState } from "./inflight-tracker";
@@ -80,7 +81,7 @@ export class EventFeed {
try {
const headers: Record<string, string> = { Accept: "text/event-stream" };
if (this.cfg.apiKey) headers["Authorization"] = `Bearer ${this.cfg.apiKey}`;
const res = await fetch(`${this.cfg.baseUrl}/api/events`, {
const res = await fetchWith(this.cfg, `${this.cfg.baseUrl}/api/events`, {
headers,
signal: this.controller.signal,
});
+9
View File
@@ -0,0 +1,9 @@
import { Agent, fetch as undiciFetch } from "undici";
import { type LlamaSwapConfig } from "./util";
const INSECURE_AGENT = new Agent({ connect: { rejectUnauthorized: false } });
export function fetchWith(cfg: LlamaSwapConfig, input: string, init?: RequestInit): Promise<Response> {
if (cfg.insecure !== true) return fetch(input, init);
return undiciFetch(input, { ...(init ?? {}), dispatcher: INSECURE_AGENT }) as unknown as Promise<Response>;
}
+3 -2
View File
@@ -1,3 +1,4 @@
import { fetchWith } from "./http";
import { type LlamaSwapConfig } from "./util";
const TIMEOUT_MS = 5000;
@@ -14,7 +15,7 @@ function headersFor(cfg: LlamaSwapConfig): Record<string, string> {
}
export async function fetchMetrics(cfg: LlamaSwapConfig): Promise<string> {
const res = await fetch(`${cfg.baseUrl}/metrics`, {
const res = await fetchWith(cfg, `${cfg.baseUrl}/metrics`, {
headers: headersFor(cfg),
signal: AbortSignal.timeout(TIMEOUT_MS),
});
@@ -23,7 +24,7 @@ export async function fetchMetrics(cfg: LlamaSwapConfig): Promise<string> {
}
export async function fetchModels(cfg: LlamaSwapConfig): Promise<ModelInfo[]> {
const res = await fetch(`${cfg.baseUrl}/v1/models`, {
const res = await fetchWith(cfg, `${cfg.baseUrl}/v1/models`, {
headers: headersFor(cfg),
signal: AbortSignal.timeout(TIMEOUT_MS),
});
+5 -1
View File
@@ -16,7 +16,11 @@ class Runtime {
private listeners = new Set<() => void>();
ensureConnections(cfg: LlamaSwapConfig): void {
const changed = !this.cfg || this.cfg.baseUrl !== cfg.baseUrl || this.cfg.apiKey !== cfg.apiKey;
const changed =
!this.cfg ||
this.cfg.baseUrl !== cfg.baseUrl ||
this.cfg.apiKey !== cfg.apiKey ||
this.cfg.insecure !== cfg.insecure;
if (changed) {
this.feed?.stop();
this.poller?.stop();
+2 -1
View File
@@ -1,3 +1,4 @@
import { fetchWith } from "./http";
import { type LlamaSwapConfig } from "./util";
export interface UsageStats {
@@ -24,7 +25,7 @@ export async function fetchStats(cfg: LlamaSwapConfig, modelId: string): Promise
const query = modelId === "all" ? "" : `?model=${encodeURIComponent(modelId)}`;
const headers: Record<string, string> = {};
if (cfg.apiKey) headers["Authorization"] = `Bearer ${cfg.apiKey}`;
const res = await fetch(`${cfg.baseUrl}/api/metrics/stats${query}`, {
const res = await fetchWith(cfg, `${cfg.baseUrl}/api/metrics/stats${query}`, {
headers,
signal: AbortSignal.timeout(5000),
});
+3
View File
@@ -5,17 +5,20 @@ export const DEFAULT_BASE_URL = "http://localhost:9292";
export type CfgSettings = {
baseUrl?: string;
apiKey?: string;
insecure?: boolean;
} & JsonObject;
export interface LlamaSwapConfig {
baseUrl: string;
apiKey?: string;
insecure?: boolean;
}
export function cfgFromSettings(settings: CfgSettings): LlamaSwapConfig {
return {
baseUrl: normalizeBaseUrl(settings.baseUrl ?? DEFAULT_BASE_URL),
apiKey: settings.apiKey || undefined,
insecure: settings.insecure === true,
};
}