mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 12:15:57 +00:00
273 lines
8.3 KiB
TypeScript
273 lines
8.3 KiB
TypeScript
import i18n from '@/lib/i18n';
|
|
|
|
export interface CodexTopLevelSettingsView {
|
|
model: string | null;
|
|
modelReasoningEffort: string | null;
|
|
modelContextWindow: number | null;
|
|
modelAutoCompactTokenLimit: number | null;
|
|
modelProvider: string | null;
|
|
approvalPolicy: string | null;
|
|
sandboxMode: string | null;
|
|
webSearch: string | null;
|
|
toolOutputTokenLimit: number | null;
|
|
personality: string | null;
|
|
}
|
|
|
|
export interface CodexProjectTrustEntry {
|
|
path: string;
|
|
trustLevel: string;
|
|
}
|
|
|
|
export interface CodexProfileEntry {
|
|
name: string;
|
|
values: CodexTopLevelSettingsView;
|
|
}
|
|
|
|
export interface CodexModelProviderEntry {
|
|
name: string;
|
|
displayName: string | null;
|
|
baseUrl: string | null;
|
|
envKey: string | null;
|
|
wireApi: string | null;
|
|
requiresOpenaiAuth: boolean;
|
|
supportsWebsockets: boolean;
|
|
}
|
|
|
|
export interface CodexMcpServerEntry {
|
|
name: string;
|
|
transport: 'stdio' | 'streamable-http';
|
|
command: string | null;
|
|
args: string[];
|
|
url: string | null;
|
|
enabled: boolean;
|
|
required: boolean;
|
|
startupTimeoutSec: number | null;
|
|
toolTimeoutSec: number | null;
|
|
enabledTools: string[];
|
|
disabledTools: string[];
|
|
isCcsManaged: boolean;
|
|
managementSurface: 'browser-settings' | null;
|
|
}
|
|
|
|
export interface CodexFeatureCatalogEntry {
|
|
name: string;
|
|
label: string;
|
|
description: string;
|
|
}
|
|
|
|
export const CLIPROXY_NATIVE_CODEX_RECIPE = `model_provider = "cliproxy"
|
|
|
|
[model_providers.cliproxy]
|
|
base_url = "http://127.0.0.1:8317/api/provider/codex"
|
|
env_key = "CLIPROXY_API_KEY"
|
|
wire_api = "responses"`;
|
|
|
|
export const KNOWN_CODEX_FEATURE_NAMES = [
|
|
'multi_agent',
|
|
'unified_exec',
|
|
'shell_snapshot',
|
|
'apply_patch_freeform',
|
|
'js_repl',
|
|
'runtime_metrics',
|
|
'prevent_idle_sleep',
|
|
'fast_mode',
|
|
'apps',
|
|
'smart_approvals',
|
|
] as const;
|
|
|
|
export function getKnownCodexFeatures(): CodexFeatureCatalogEntry[] {
|
|
return [
|
|
{
|
|
name: 'multi_agent',
|
|
label: i18n.t('codex.featureMultiAgentLabel'),
|
|
description: i18n.t('codex.featureMultiAgentDesc'),
|
|
},
|
|
{
|
|
name: 'unified_exec',
|
|
label: i18n.t('codex.featureUnifiedExecLabel'),
|
|
description: i18n.t('codex.featureUnifiedExecDesc'),
|
|
},
|
|
{
|
|
name: 'shell_snapshot',
|
|
label: i18n.t('codex.featureShellSnapshotLabel'),
|
|
description: i18n.t('codex.featureShellSnapshotDesc'),
|
|
},
|
|
{
|
|
name: 'apply_patch_freeform',
|
|
label: i18n.t('codex.featureApplyPatchLabel'),
|
|
description: i18n.t('codex.featureApplyPatchDesc'),
|
|
},
|
|
{
|
|
name: 'js_repl',
|
|
label: i18n.t('codex.featureJsReplLabel'),
|
|
description: i18n.t('codex.featureJsReplDesc'),
|
|
},
|
|
{
|
|
name: 'runtime_metrics',
|
|
label: i18n.t('codex.featureRuntimeMetricsLabel'),
|
|
description: i18n.t('codex.featureRuntimeMetricsDesc'),
|
|
},
|
|
{
|
|
name: 'prevent_idle_sleep',
|
|
label: i18n.t('codex.featurePreventIdleSleepLabel'),
|
|
description: i18n.t('codex.featurePreventIdleSleepDesc'),
|
|
},
|
|
{
|
|
name: 'fast_mode',
|
|
label: i18n.t('codex.featureFastModeLabel'),
|
|
description: i18n.t('codex.featureFastModeDesc'),
|
|
},
|
|
{
|
|
name: 'apps',
|
|
label: i18n.t('codex.featureAppsLabel'),
|
|
description: i18n.t('codex.featureAppsDesc'),
|
|
},
|
|
{
|
|
name: 'smart_approvals',
|
|
label: i18n.t('codex.featureSmartApprovalsLabel'),
|
|
description: i18n.t('codex.featureSmartApprovalsDesc'),
|
|
},
|
|
];
|
|
}
|
|
|
|
function asObject(value: unknown): Record<string, unknown> | null {
|
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: null;
|
|
}
|
|
|
|
function asString(value: unknown): string | null {
|
|
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
|
|
}
|
|
|
|
function asNumber(value: unknown): number | null {
|
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
}
|
|
|
|
function asStringArray(value: unknown): string[] {
|
|
return Array.isArray(value)
|
|
? value
|
|
.filter((entry): entry is string => typeof entry === 'string')
|
|
.map((entry) => entry.trim())
|
|
: [];
|
|
}
|
|
|
|
export function readCodexTopLevelSettings(
|
|
config: Record<string, unknown> | null
|
|
): CodexTopLevelSettingsView {
|
|
return {
|
|
model: asString(config?.model),
|
|
modelReasoningEffort: asString(config?.model_reasoning_effort),
|
|
modelContextWindow: asNumber(config?.model_context_window),
|
|
modelAutoCompactTokenLimit: asNumber(config?.model_auto_compact_token_limit),
|
|
modelProvider: asString(config?.model_provider),
|
|
approvalPolicy: asString(config?.approval_policy),
|
|
sandboxMode: asString(config?.sandbox_mode),
|
|
webSearch: asString(config?.web_search),
|
|
toolOutputTokenLimit: asNumber(config?.tool_output_token_limit),
|
|
personality: asString(config?.personality),
|
|
};
|
|
}
|
|
|
|
export function readCodexProjectTrust(
|
|
config: Record<string, unknown> | null
|
|
): CodexProjectTrustEntry[] {
|
|
const projects = asObject(config?.projects);
|
|
if (!projects) return [];
|
|
|
|
return Object.entries(projects)
|
|
.map(([projectPath, value]) => {
|
|
const trustLevel = asString(asObject(value)?.trust_level);
|
|
return trustLevel ? { path: projectPath, trustLevel } : null;
|
|
})
|
|
.filter((entry): entry is CodexProjectTrustEntry => entry !== null)
|
|
.sort((left, right) => left.path.localeCompare(right.path));
|
|
}
|
|
|
|
export function readCodexProfiles(config: Record<string, unknown> | null): CodexProfileEntry[] {
|
|
const profiles = asObject(config?.profiles);
|
|
if (!profiles) return [];
|
|
|
|
return Object.entries(profiles)
|
|
.map(([name, value]) => ({ name, values: readCodexTopLevelSettings(asObject(value)) }))
|
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
}
|
|
|
|
export function readCodexModelProviders(
|
|
config: Record<string, unknown> | null
|
|
): CodexModelProviderEntry[] {
|
|
const providers = asObject(config?.model_providers);
|
|
if (!providers) return [];
|
|
|
|
return Object.entries(providers)
|
|
.map(([name, value]) => {
|
|
const provider = asObject(value);
|
|
if (!provider) return null;
|
|
return {
|
|
name,
|
|
displayName: asString(provider.name),
|
|
baseUrl: asString(provider.base_url),
|
|
envKey: asString(provider.env_key),
|
|
wireApi: asString(provider.wire_api),
|
|
requiresOpenaiAuth: provider.requires_openai_auth === true,
|
|
supportsWebsockets: provider.supports_websockets === true,
|
|
};
|
|
})
|
|
.filter((entry): entry is CodexModelProviderEntry => entry !== null)
|
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
}
|
|
|
|
export function readCodexMcpServers(config: Record<string, unknown> | null): CodexMcpServerEntry[] {
|
|
const servers = asObject(config?.mcp_servers);
|
|
if (!servers) return [];
|
|
|
|
return Object.entries(servers)
|
|
.map(([name, value]) => {
|
|
const server = asObject(value);
|
|
if (!server) return null;
|
|
const transport = asString(server.command) ? 'stdio' : 'streamable-http';
|
|
const startupTimeoutMs = asNumber(server.startup_timeout_ms);
|
|
return {
|
|
name,
|
|
transport,
|
|
command: asString(server.command),
|
|
args: asStringArray(server.args),
|
|
url: asString(server.url),
|
|
enabled: server.enabled !== false,
|
|
required: server.required === true,
|
|
startupTimeoutSec:
|
|
asNumber(server.startup_timeout_sec) ??
|
|
(startupTimeoutMs !== null ? startupTimeoutMs / 1000 : null),
|
|
toolTimeoutSec: asNumber(server.tool_timeout_sec),
|
|
enabledTools: asStringArray(server.enabled_tools),
|
|
disabledTools: asStringArray(server.disabled_tools),
|
|
isCcsManaged: name === 'ccs_browser',
|
|
managementSurface: name === 'ccs_browser' ? 'browser-settings' : null,
|
|
};
|
|
})
|
|
.filter((entry): entry is CodexMcpServerEntry => entry !== null)
|
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
}
|
|
|
|
export function readCodexFeatureState(
|
|
config: Record<string, unknown> | null
|
|
): Record<string, boolean | null> {
|
|
const features = asObject(config?.features);
|
|
const state: Record<string, boolean | null> = {};
|
|
|
|
for (const featureName of KNOWN_CODEX_FEATURE_NAMES) {
|
|
const value = features?.[featureName];
|
|
state[featureName] = typeof value === 'boolean' ? value : null;
|
|
}
|
|
|
|
if (features) {
|
|
for (const [name, value] of Object.entries(features)) {
|
|
if (!(name in state)) {
|
|
state[name] = typeof value === 'boolean' ? value : null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|