Files
OpenViking/examples/pi-coding-agent-extension/config.ts
T

161 lines
7.4 KiB
TypeScript

import { readFileSync, existsSync } from "node:fs";
import { join, dirname } from "node:path";
import { resolveOpenVikingCredentials } from "./shared/credentials.mjs";
import { resolveEffectivePeerId } from "./shared/workspace-peer.mjs";
export interface OVConfig {
enabled: boolean;
endpoint: string;
apiKey: string;
account: string;
user: string;
peerId: string;
workspacePeer: boolean;
recallPeerScope: "actor" | "all";
syncTurns: boolean;
recallTokenBudget: number;
recallMaxContentChars: number;
recallPreferAbstract: boolean;
recallLimit: number;
scoreThreshold: number;
minQueryLength: number;
profileTokenBudget: number;
resumeContextBudget: number;
commitTokenThreshold: number;
commitKeepRecentCount: number;
takeoverEnabled: boolean;
takeoverTokenThreshold: number;
takeoverKeepRecentTurns: number;
takeoverOverviewBudget: number;
takeoverOverviewPollMs: number;
takeoverOverviewPollMax: number;
captureToolResults: boolean;
captureMode: "semantic" | "keyword";
captureMaxLength: number;
captureToolMaxChars: number;
captureAssistantTurns: boolean;
bypassPatterns: string[];
logLevel: "silent" | "error" | "info";
}
const DEFAULT_CONFIG: OVConfig = {
enabled: true,
endpoint: "http://127.0.0.1:1933",
apiKey: "",
account: "",
user: "",
peerId: "",
workspacePeer: true,
recallPeerScope: "all",
syncTurns: true,
recallTokenBudget: 2000,
recallMaxContentChars: 500,
recallPreferAbstract: true,
recallLimit: 6,
scoreThreshold: 0.35,
minQueryLength: 3,
profileTokenBudget: 10000,
resumeContextBudget: 32000,
commitTokenThreshold: 20000,
commitKeepRecentCount: 10,
takeoverEnabled: true,
takeoverTokenThreshold: 30000,
takeoverKeepRecentTurns: 3,
takeoverOverviewBudget: 3000,
takeoverOverviewPollMs: 2000,
takeoverOverviewPollMax: 15,
captureToolResults: false,
captureMode: "semantic",
captureMaxLength: 24000,
captureToolMaxChars: 2000,
captureAssistantTurns: true,
bypassPatterns: [],
logLevel: "error",
};
export function loadConfig(extensionDir: string): OVConfig {
const configPath = join(extensionDir, "config.json");
let file: any = {};
try {
if (existsSync(configPath)) file = JSON.parse(readFileSync(configPath, "utf8"));
} catch {
file = {};
}
const takeover = file.takeover && typeof file.takeover === "object" ? file.takeover : {};
const creds = resolveOpenVikingCredentials();
const config: OVConfig = {
...DEFAULT_CONFIG,
...file,
endpoint: creds.baseUrl,
apiKey: creds.apiKey,
account: creds.account,
user: creds.user,
peerId: creds.peerId,
recallTokenBudget: file.recallTokenBudget ?? file.recallBudget ?? DEFAULT_CONFIG.recallTokenBudget,
scoreThreshold: file.scoreThreshold ?? file.recallScoreThreshold ?? DEFAULT_CONFIG.scoreThreshold,
minQueryLength: file.minQueryLength ?? file.recallMinQueryLength ?? DEFAULT_CONFIG.minQueryLength,
profileTokenBudget: file.profileTokenBudget ?? file.profileBudget ?? DEFAULT_CONFIG.profileTokenBudget,
takeoverEnabled: takeover.enabled ?? file.takeoverEnabled ?? DEFAULT_CONFIG.takeoverEnabled,
takeoverTokenThreshold: takeover.tokenThreshold ?? file.takeoverTokenThreshold ?? DEFAULT_CONFIG.takeoverTokenThreshold,
takeoverKeepRecentTurns: takeover.keepRecentTurns ?? file.takeoverKeepRecentTurns ?? DEFAULT_CONFIG.takeoverKeepRecentTurns,
takeoverOverviewBudget: takeover.overviewBudget ?? file.takeoverOverviewBudget ?? DEFAULT_CONFIG.takeoverOverviewBudget,
takeoverOverviewPollMs: takeover.overviewPollMs ?? file.takeoverOverviewPollMs ?? DEFAULT_CONFIG.takeoverOverviewPollMs,
takeoverOverviewPollMax: takeover.overviewPollMax ?? file.takeoverOverviewPollMax ?? DEFAULT_CONFIG.takeoverOverviewPollMax,
};
if (process.env.OPENVIKING_URL || process.env.OPENVIKING_BASE_URL) config.endpoint = creds.baseUrl;
if (process.env.OPENVIKING_API_KEY || process.env.OPENVIKING_BEARER_TOKEN) config.apiKey = creds.apiKey;
if (process.env.OPENVIKING_ACCOUNT) config.account = creds.account;
if (process.env.OPENVIKING_USER) config.user = creds.user;
if (process.env.OPENVIKING_PEER_ID) config.peerId = creds.peerId;
if (process.env.OPENVIKING_WORKSPACE_PEER !== undefined) {
config.workspacePeer = envBool(process.env.OPENVIKING_WORKSPACE_PEER, config.workspacePeer);
}
if (process.env.OPENVIKING_RECALL_PEER_SCOPE) {
config.recallPeerScope = process.env.OPENVIKING_RECALL_PEER_SCOPE === "actor" ? "actor" : "all";
}
config.recallLimit = clampInt(config.recallLimit, 1, 50, DEFAULT_CONFIG.recallLimit);
config.recallMaxContentChars = clampInt(config.recallMaxContentChars, 100, 5000, DEFAULT_CONFIG.recallMaxContentChars);
config.recallTokenBudget = clampInt(config.recallTokenBudget, 200, 50000, DEFAULT_CONFIG.recallTokenBudget);
config.scoreThreshold = clampNumber(config.scoreThreshold, 0, 1, DEFAULT_CONFIG.scoreThreshold);
config.minQueryLength = clampInt(config.minQueryLength, 1, 64, DEFAULT_CONFIG.minQueryLength);
config.profileTokenBudget = clampInt(config.profileTokenBudget, 500, 50000, DEFAULT_CONFIG.profileTokenBudget);
config.resumeContextBudget = clampInt(config.resumeContextBudget, 1024, 128000, DEFAULT_CONFIG.resumeContextBudget);
config.commitTokenThreshold = clampInt(config.commitTokenThreshold, 1000, 1000000, DEFAULT_CONFIG.commitTokenThreshold);
config.commitKeepRecentCount = clampInt(config.commitKeepRecentCount, 0, 1000, DEFAULT_CONFIG.commitKeepRecentCount);
config.takeoverEnabled = config.takeoverEnabled !== false;
config.takeoverTokenThreshold = clampInt(config.takeoverTokenThreshold, 1, 1000000, DEFAULT_CONFIG.takeoverTokenThreshold);
config.takeoverKeepRecentTurns = clampInt(config.takeoverKeepRecentTurns, 0, 100, DEFAULT_CONFIG.takeoverKeepRecentTurns);
config.takeoverOverviewBudget = clampInt(config.takeoverOverviewBudget, 100, 50000, DEFAULT_CONFIG.takeoverOverviewBudget);
config.takeoverOverviewPollMs = clampInt(config.takeoverOverviewPollMs, 0, 60000, DEFAULT_CONFIG.takeoverOverviewPollMs);
config.takeoverOverviewPollMax = clampInt(config.takeoverOverviewPollMax, 1, 120, DEFAULT_CONFIG.takeoverOverviewPollMax);
config.captureMaxLength = clampInt(config.captureMaxLength, 200, 100000, DEFAULT_CONFIG.captureMaxLength);
config.captureToolMaxChars = clampInt(config.captureToolMaxChars, 200, 20000, DEFAULT_CONFIG.captureToolMaxChars);
config.captureMode = config.captureMode === "keyword" ? "keyword" : "semantic";
config.recallPeerScope = config.recallPeerScope === "actor" ? "actor" : "all";
if (!Array.isArray(config.bypassPatterns)) config.bypassPatterns = [];
config.peerId = resolveEffectivePeerId({ cfg: config as any, cwd: process.cwd() }).peerId;
return config;
}
function envBool(value: string, fallback: boolean): boolean {
const lower = String(value || "").trim().toLowerCase();
if (lower === "0" || lower === "false" || lower === "no" || lower === "off") return false;
if (lower === "1" || lower === "true" || lower === "yes" || lower === "on") return true;
return fallback;
}
function clampInt(value: unknown, min: number, max: number, fallback: number): number {
const next = Math.round(Number(value));
if (!Number.isFinite(next)) return fallback;
return Math.max(min, Math.min(max, next));
}
function clampNumber(value: unknown, min: number, max: number, fallback: number): number {
const next = Number(value);
if (!Number.isFinite(next)) return fallback;
return Math.max(min, Math.min(max, next));
}