mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
Type runtime config projection against app-server config
This commit is contained in:
parent
bde7b65974
commit
f000fa7e75
2 changed files with 60 additions and 37 deletions
|
|
@ -1,73 +1,86 @@
|
|||
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
|
||||
import type { ReasoningSummary } from "../generated/app-server/ReasoningSummary";
|
||||
import type { Verbosity } from "../generated/app-server/Verbosity";
|
||||
import type { WebSearchMode } from "../generated/app-server/WebSearchMode";
|
||||
import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse";
|
||||
import type { AppsConfig } from "../generated/app-server/v2/AppsConfig";
|
||||
import type { AskForApproval } from "../generated/app-server/v2/AskForApproval";
|
||||
import type { ApprovalsReviewer } from "../generated/app-server/v2/ApprovalsReviewer";
|
||||
import type { Config } from "../generated/app-server/v2/Config";
|
||||
import type { ProfileV2 } from "../generated/app-server/v2/ProfileV2";
|
||||
import type { SandboxMode } from "../generated/app-server/v2/SandboxMode";
|
||||
import type { SandboxWorkspaceWrite } from "../generated/app-server/v2/SandboxWorkspaceWrite";
|
||||
import type { ToolsV2 } from "../generated/app-server/v2/ToolsV2";
|
||||
import { parseServiceTier, type ServiceTier } from "../app-server/service-tier";
|
||||
import { isReasoningEffort } from "./model";
|
||||
|
||||
export interface RuntimeConfigProjection {
|
||||
profile: string | null;
|
||||
model: string | null;
|
||||
modelProvider: unknown;
|
||||
modelProvider: string | null;
|
||||
reasoningEffort: ReasoningEffort | null;
|
||||
rawReasoningEffort: string | null;
|
||||
reasoningSummary: unknown;
|
||||
verbosity: unknown;
|
||||
reasoningSummary: ReasoningSummary | null;
|
||||
verbosity: Verbosity | null;
|
||||
serviceTier: ServiceTier | null;
|
||||
approvalsReviewer: ApprovalsReviewer | null;
|
||||
approvalPolicy: unknown;
|
||||
webSearch: unknown;
|
||||
approvalPolicy: AskForApproval | null;
|
||||
webSearch: WebSearchMode | null;
|
||||
modelContextWindow: number | null;
|
||||
autoCompactTokenLimit: number | null;
|
||||
sandboxMode: unknown;
|
||||
workspaceNetworkAccess: unknown;
|
||||
writableRoots: unknown;
|
||||
sandboxMode: SandboxMode | null;
|
||||
workspaceNetworkAccess: SandboxWorkspaceWrite["network_access"] | null;
|
||||
writableRoots: SandboxWorkspaceWrite["writable_roots"] | null;
|
||||
hooksEnabled: unknown;
|
||||
applyPatchFreeformEnabled: unknown;
|
||||
toolWebSearch: unknown;
|
||||
apps: Record<string, unknown>;
|
||||
toolWebSearch: ToolsV2["web_search"] | null;
|
||||
apps: AppsConfig | null;
|
||||
}
|
||||
|
||||
export function readRuntimeConfig(effectiveConfig: ConfigReadResponse | null): RuntimeConfigProjection {
|
||||
const config = configRecord(effectiveConfig);
|
||||
const features = asRecord(config["features"]);
|
||||
const tools = asRecord(resolvedConfigValue(config, "tools"));
|
||||
const workspaceWrite = asRecord(config["sandbox_workspace_write"]);
|
||||
const tools = resolvedConfigValue(config, "tools") ?? null;
|
||||
const workspaceWrite = config.sandbox_workspace_write ?? null;
|
||||
const effort = resolvedConfigValue(config, "model_reasoning_effort");
|
||||
return {
|
||||
profile: stringOrNull(config["profile"]),
|
||||
profile: stringOrNull(config.profile),
|
||||
model: nonEmptyStringOrNull(resolvedConfigValue(config, "model")),
|
||||
modelProvider: resolvedConfigValue(config, "model_provider"),
|
||||
modelProvider: resolvedConfigValue(config, "model_provider") ?? null,
|
||||
reasoningEffort: isReasoningEffort(effort) ? effort : null,
|
||||
rawReasoningEffort: nonEmptyStringOrNull(effort),
|
||||
reasoningSummary: resolvedConfigValue(config, "model_reasoning_summary"),
|
||||
verbosity: resolvedConfigValue(config, "model_verbosity"),
|
||||
reasoningSummary: resolvedConfigValue(config, "model_reasoning_summary") ?? null,
|
||||
verbosity: resolvedConfigValue(config, "model_verbosity") ?? null,
|
||||
serviceTier: parseServiceTier(resolvedConfigValue(config, "service_tier")),
|
||||
approvalsReviewer: approvalsReviewerOrNull(resolvedConfigValue(config, "approvals_reviewer")),
|
||||
approvalPolicy: resolvedConfigValue(config, "approval_policy"),
|
||||
webSearch: resolvedConfigValue(config, "web_search"),
|
||||
modelContextWindow: numberOrNull(config["model_context_window"]),
|
||||
autoCompactTokenLimit: numberOrNull(config["model_auto_compact_token_limit"]),
|
||||
sandboxMode: config["sandbox_mode"],
|
||||
workspaceNetworkAccess: workspaceWrite["network_access"],
|
||||
writableRoots: workspaceWrite["writable_roots"],
|
||||
approvalPolicy: resolvedConfigValue(config, "approval_policy") ?? null,
|
||||
webSearch: resolvedConfigValue(config, "web_search") ?? null,
|
||||
modelContextWindow: numberOrNull(config.model_context_window),
|
||||
autoCompactTokenLimit: numberOrNull(config.model_auto_compact_token_limit),
|
||||
sandboxMode: config.sandbox_mode ?? null,
|
||||
workspaceNetworkAccess: workspaceWrite?.network_access ?? null,
|
||||
writableRoots: workspaceWrite?.writable_roots ?? null,
|
||||
hooksEnabled: features["hooks"],
|
||||
applyPatchFreeformEnabled: features["apply_patch_freeform"],
|
||||
toolWebSearch: tools["web_search"],
|
||||
apps: asRecord(config["apps"]),
|
||||
toolWebSearch: tools?.web_search ?? null,
|
||||
apps: config.apps ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function configRecord(effectiveConfig: ConfigReadResponse | null): Record<string, unknown> {
|
||||
return fillMissingConfigValues(asRecord(effectiveConfig?.config), effectiveConfig?.layers ?? null);
|
||||
type ConfigProjectionRecord = Partial<Config> & Record<string, unknown>;
|
||||
type ProfileProjectionRecord = Partial<ProfileV2> & Record<string, unknown>;
|
||||
|
||||
function configRecord(effectiveConfig: ConfigReadResponse | null): ConfigProjectionRecord {
|
||||
return fillMissingConfigValues(asConfigRecord(effectiveConfig?.config), effectiveConfig?.layers ?? null);
|
||||
}
|
||||
|
||||
function selectedProfileConfig(config: Record<string, unknown>): Record<string, unknown> {
|
||||
const profile = config["profile"];
|
||||
function selectedProfileConfig(config: ConfigProjectionRecord): ProfileProjectionRecord {
|
||||
const profile = config.profile;
|
||||
const profileName = typeof profile === "string" && profile.length > 0 ? profile : null;
|
||||
return profileName ? asRecord(asRecord(config["profiles"])[profileName]) : {};
|
||||
return profileName ? asProfileRecord(asRecord(config.profiles)[profileName]) : {};
|
||||
}
|
||||
|
||||
function resolvedConfigValue(config: Record<string, unknown>, key: string): unknown {
|
||||
function resolvedConfigValue<K extends keyof Config>(config: ConfigProjectionRecord, key: K): Config[K] | undefined {
|
||||
const profileValue = selectedProfileConfig(config)[key];
|
||||
return profileValue ?? config[key];
|
||||
}
|
||||
|
|
@ -80,14 +93,23 @@ function asRecord(value: unknown): Record<string, unknown> {
|
|||
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
function fillMissingConfigValues(config: Record<string, unknown>, layers: ConfigReadResponse["layers"]): Record<string, unknown> {
|
||||
function asConfigRecord(value: unknown): ConfigProjectionRecord {
|
||||
return asRecord(value) as ConfigProjectionRecord;
|
||||
}
|
||||
|
||||
function asProfileRecord(value: unknown): ProfileProjectionRecord {
|
||||
return asRecord(value) as ProfileProjectionRecord;
|
||||
}
|
||||
|
||||
function fillMissingConfigValues(config: ConfigProjectionRecord, layers: ConfigReadResponse["layers"]): ConfigProjectionRecord {
|
||||
if (!layers || layers.length === 0) return config;
|
||||
const fallback = rawLayerConfigRecord(layers);
|
||||
if (Object.keys(fallback).length === 0) return config;
|
||||
|
||||
const merged = { ...config };
|
||||
const merged: ConfigProjectionRecord = { ...config };
|
||||
const mergedRecord = merged as Record<string, unknown>;
|
||||
for (const [key, value] of Object.entries(fallback)) {
|
||||
merged[key] ??= value;
|
||||
mergedRecord[key] ??= value;
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,13 +262,14 @@ function writableRootsLabel(value: unknown): string {
|
|||
return `${String(value.length)} roots`;
|
||||
}
|
||||
|
||||
function enabledAppsLabel(apps: Record<string, unknown>): string {
|
||||
const enabled = Object.entries(apps)
|
||||
function enabledAppsLabel(apps: unknown): string {
|
||||
const appConfig = asRecord(apps);
|
||||
const enabled = Object.entries(appConfig)
|
||||
.filter(([key, app]) => key !== "_default" && asRecord(app)["enabled"] === true)
|
||||
.map(([key]) => key)
|
||||
.sort();
|
||||
if (enabled.length > 0) return enabled.join(", ");
|
||||
const defaultConfig = asRecord(apps["_default"]);
|
||||
const defaultConfig = asRecord(appConfig["_default"]);
|
||||
return stringValue(defaultConfig["enabled"], "(from default)");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue