export type JsonRecord = Record export interface CoreConfigForm { workspace: string restrictToWorkspace: boolean allowRemote: boolean allowCommand: boolean cronExecTimeoutMinutes: string maxTokens: string maxToolIterations: string summarizeMessageThreshold: string summarizeTokenPercent: string dmScope: string heartbeatEnabled: boolean heartbeatInterval: string devicesEnabled: boolean monitorUSB: boolean } export interface LauncherForm { port: string publicAccess: boolean allowedCIDRsText: string } export const DM_SCOPE_OPTIONS = [ { value: "per-channel-peer", labelKey: "pages.config.session_scope_per_channel_peer", labelDefault: "Per Channel + Peer", descKey: "pages.config.session_scope_per_channel_peer_desc", descDefault: "Separate context for each user in each channel.", }, { value: "per-channel", labelKey: "pages.config.session_scope_per_channel", labelDefault: "Per Channel", descKey: "pages.config.session_scope_per_channel_desc", descDefault: "One shared context per channel.", }, { value: "per-peer", labelKey: "pages.config.session_scope_per_peer", labelDefault: "Per Peer", descKey: "pages.config.session_scope_per_peer_desc", descDefault: "One context per user across channels.", }, { value: "global", labelKey: "pages.config.session_scope_global", labelDefault: "Global", descKey: "pages.config.session_scope_global_desc", descDefault: "All messages share one global context.", }, ] as const export const EMPTY_FORM: CoreConfigForm = { workspace: "", restrictToWorkspace: true, allowRemote: true, allowCommand: true, cronExecTimeoutMinutes: "5", maxTokens: "32768", maxToolIterations: "50", summarizeMessageThreshold: "20", summarizeTokenPercent: "75", dmScope: "per-channel-peer", heartbeatEnabled: true, heartbeatInterval: "30", devicesEnabled: false, monitorUSB: true, } export const EMPTY_LAUNCHER_FORM: LauncherForm = { port: "18800", publicAccess: false, allowedCIDRsText: "", } function asRecord(value: unknown): JsonRecord { if (value && typeof value === "object" && !Array.isArray(value)) { return value as JsonRecord } return {} } function asString(value: unknown): string { return typeof value === "string" ? value : "" } function asBool(value: unknown): boolean { return value === true } function asNumberString(value: unknown, fallback: string): string { if (typeof value === "number" && Number.isFinite(value)) { return String(value) } if (typeof value === "string" && value.trim() !== "") { return value } return fallback } export function buildFormFromConfig(config: unknown): CoreConfigForm { const root = asRecord(config) const agents = asRecord(root.agents) const defaults = asRecord(agents.defaults) const session = asRecord(root.session) const heartbeat = asRecord(root.heartbeat) const devices = asRecord(root.devices) const tools = asRecord(root.tools) const cron = asRecord(tools.cron) const exec = asRecord(tools.exec) return { workspace: asString(defaults.workspace) || EMPTY_FORM.workspace, restrictToWorkspace: defaults.restrict_to_workspace === undefined ? EMPTY_FORM.restrictToWorkspace : asBool(defaults.restrict_to_workspace), allowRemote: exec.allow_remote === undefined ? EMPTY_FORM.allowRemote : asBool(exec.allow_remote), allowCommand: cron.allow_command === undefined ? EMPTY_FORM.allowCommand : asBool(cron.allow_command), cronExecTimeoutMinutes: asNumberString( cron.exec_timeout_minutes, EMPTY_FORM.cronExecTimeoutMinutes, ), maxTokens: asNumberString(defaults.max_tokens, EMPTY_FORM.maxTokens), maxToolIterations: asNumberString( defaults.max_tool_iterations, EMPTY_FORM.maxToolIterations, ), summarizeMessageThreshold: asNumberString( defaults.summarize_message_threshold, EMPTY_FORM.summarizeMessageThreshold, ), summarizeTokenPercent: asNumberString( defaults.summarize_token_percent, EMPTY_FORM.summarizeTokenPercent, ), dmScope: asString(session.dm_scope) || EMPTY_FORM.dmScope, heartbeatEnabled: heartbeat.enabled === undefined ? EMPTY_FORM.heartbeatEnabled : asBool(heartbeat.enabled), heartbeatInterval: asNumberString( heartbeat.interval, EMPTY_FORM.heartbeatInterval, ), devicesEnabled: devices.enabled === undefined ? EMPTY_FORM.devicesEnabled : asBool(devices.enabled), monitorUSB: devices.monitor_usb === undefined ? EMPTY_FORM.monitorUSB : asBool(devices.monitor_usb), } } export function parseIntField( rawValue: string, label: string, options: { min?: number; max?: number } = {}, ): number { const value = Number(rawValue) if (!Number.isInteger(value)) { throw new Error(`${label} must be an integer.`) } if (options.min !== undefined && value < options.min) { throw new Error(`${label} must be >= ${options.min}.`) } if (options.max !== undefined && value > options.max) { throw new Error(`${label} must be <= ${options.max}.`) } return value } export function parseCIDRText(raw: string): string[] { if (!raw.trim()) { return [] } return raw .split(/[\n,]/) .map((v) => v.trim()) .filter((v) => v.length > 0) }