129 lines
3.3 KiB
TypeScript
129 lines
3.3 KiB
TypeScript
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { dirname } from "node:path";
|
|
|
|
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
|
|
|
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
|
|
export function parseModelSpec(spec: string, modelRegistry: ModelRegistry) {
|
|
const trimmed = spec.trim();
|
|
const separator = trimmed.includes(":") ? ":" : trimmed.includes("/") ? "/" : null;
|
|
if (!separator) {
|
|
return undefined;
|
|
}
|
|
|
|
const [provider, ...rest] = trimmed.split(separator);
|
|
const id = rest.join(separator);
|
|
if (!provider || !id) {
|
|
return undefined;
|
|
}
|
|
|
|
return modelRegistry.find(provider, id);
|
|
}
|
|
|
|
export function normalizeThinkingLevel(value: string | undefined): ThinkingLevel | undefined {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
|
|
const normalized = value.toLowerCase();
|
|
if (
|
|
normalized === "off" ||
|
|
normalized === "minimal" ||
|
|
normalized === "low" ||
|
|
normalized === "medium" ||
|
|
normalized === "high" ||
|
|
normalized === "xhigh"
|
|
) {
|
|
return normalized;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function choosePreferredModel(
|
|
availableModels: Array<{ provider: string; id: string }>,
|
|
): { provider: string; id: string } | undefined {
|
|
const preferences = [
|
|
{ provider: "anthropic", id: "claude-opus-4-6" },
|
|
{ provider: "anthropic", id: "claude-opus-4-5" },
|
|
{ provider: "anthropic", id: "claude-sonnet-4-5" },
|
|
{ provider: "openai", id: "gpt-5.4" },
|
|
{ provider: "openai", id: "gpt-5" },
|
|
];
|
|
|
|
for (const preferred of preferences) {
|
|
const match = availableModels.find(
|
|
(model) => model.provider === preferred.provider && model.id === preferred.id,
|
|
);
|
|
if (match) {
|
|
return match;
|
|
}
|
|
}
|
|
|
|
return availableModels[0];
|
|
}
|
|
|
|
export function readJson(path: string): Record<string, unknown> {
|
|
if (!existsSync(path)) {
|
|
return {};
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
export function normalizeFeynmanSettings(
|
|
settingsPath: string,
|
|
bundledSettingsPath: string,
|
|
defaultThinkingLevel: ThinkingLevel,
|
|
authPath: string,
|
|
): void {
|
|
let settings: Record<string, unknown> = {};
|
|
|
|
if (existsSync(settingsPath)) {
|
|
try {
|
|
settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
} catch {
|
|
settings = {};
|
|
}
|
|
} else if (existsSync(bundledSettingsPath)) {
|
|
try {
|
|
settings = JSON.parse(readFileSync(bundledSettingsPath, "utf8"));
|
|
} catch {
|
|
settings = {};
|
|
}
|
|
}
|
|
|
|
if (!settings.defaultThinkingLevel) {
|
|
settings.defaultThinkingLevel = defaultThinkingLevel;
|
|
}
|
|
if (settings.editorPaddingX === undefined) {
|
|
settings.editorPaddingX = 1;
|
|
}
|
|
settings.theme = "feynman";
|
|
settings.quietStartup = true;
|
|
settings.collapseChangelog = true;
|
|
|
|
const authStorage = AuthStorage.create(authPath);
|
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
const availableModels = modelRegistry.getAvailable().map((model) => ({
|
|
provider: model.provider,
|
|
id: model.id,
|
|
}));
|
|
|
|
if ((!settings.defaultProvider || !settings.defaultModel) && availableModels.length > 0) {
|
|
const preferredModel = choosePreferredModel(availableModels);
|
|
if (preferredModel) {
|
|
settings.defaultProvider = preferredModel.provider;
|
|
settings.defaultModel = preferredModel.id;
|
|
}
|
|
}
|
|
|
|
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
}
|