zcf 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,12 +13,12 @@ import toggleModule from 'inquirer-toggle';
13
13
  import ora from 'ora';
14
14
  import { exec, x } from 'tinyexec';
15
15
  import semver from 'semver';
16
- import { stringify, parse } from 'smol-toml';
16
+ import { edit, stringify, parse, initSync } from '@rainbowatcher/toml-edit-js';
17
17
  import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
18
18
  import i18next from 'i18next';
19
19
  import Backend from 'i18next-fs-backend';
20
20
 
21
- const version = "3.5.0";
21
+ const version = "3.6.0";
22
22
  const homepage = "https://github.com/UfoMiao/zcf";
23
23
 
24
24
  const i18n = i18next.createInstance();
@@ -2850,6 +2850,34 @@ function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
2850
2850
  });
2851
2851
  }
2852
2852
 
2853
+ let initialized = false;
2854
+ function ensureTomlInitSync() {
2855
+ if (!initialized) {
2856
+ initSync();
2857
+ initialized = true;
2858
+ }
2859
+ }
2860
+ function parseToml(content) {
2861
+ ensureTomlInitSync();
2862
+ return parse(content);
2863
+ }
2864
+ function stringifyToml(data) {
2865
+ ensureTomlInitSync();
2866
+ return stringify(data);
2867
+ }
2868
+ function editToml(content, path, value) {
2869
+ ensureTomlInitSync();
2870
+ return edit(content, path, value);
2871
+ }
2872
+ function batchEditToml(content, edits) {
2873
+ ensureTomlInitSync();
2874
+ let result = content;
2875
+ for (const [path, value] of edits) {
2876
+ result = edit(result, path, value);
2877
+ }
2878
+ return result;
2879
+ }
2880
+
2853
2881
  function isSupportedLang(value) {
2854
2882
  return SUPPORTED_LANGS.includes(value);
2855
2883
  }
@@ -2865,18 +2893,117 @@ function readTomlConfig(configPath) {
2865
2893
  return null;
2866
2894
  }
2867
2895
  const content = readFile(configPath);
2868
- const parsed = parse(content);
2896
+ const parsed = parseToml(content);
2869
2897
  return parsed;
2870
2898
  } catch {
2871
2899
  return null;
2872
2900
  }
2873
2901
  }
2902
+ function insertAtTopLevelStart(topLevel, content) {
2903
+ const lines = topLevel.split("\n");
2904
+ let insertLineIndex = 0;
2905
+ for (let i = 0; i < lines.length; i++) {
2906
+ const trimmed = lines[i].trim();
2907
+ if (trimmed === "" || trimmed.startsWith("#")) {
2908
+ insertLineIndex = i + 1;
2909
+ } else {
2910
+ insertLineIndex = i;
2911
+ break;
2912
+ }
2913
+ }
2914
+ lines.splice(insertLineIndex, 0, content.replace(/\n$/, ""));
2915
+ return lines.join("\n");
2916
+ }
2917
+ function insertAfterVersionField(topLevel, content) {
2918
+ const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m;
2919
+ const match = topLevel.match(versionRegex);
2920
+ if (match && match.index !== void 0) {
2921
+ const versionEnd = match.index + match[0].length;
2922
+ const before = topLevel.slice(0, versionEnd);
2923
+ const after = topLevel.slice(versionEnd);
2924
+ return `${before}
2925
+ ${content.replace(/\n$/, "")}${after}`;
2926
+ }
2927
+ return insertAtTopLevelStart(topLevel, content);
2928
+ }
2929
+ function updateTopLevelTomlFields(content, version, lastUpdated) {
2930
+ const firstSectionMatch = content.match(/^\[/m);
2931
+ const topLevelEnd = firstSectionMatch?.index ?? content.length;
2932
+ let topLevel = content.slice(0, topLevelEnd);
2933
+ const rest = content.slice(topLevelEnd);
2934
+ const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m;
2935
+ const versionMatch = topLevel.match(versionRegex);
2936
+ if (versionMatch) {
2937
+ topLevel = topLevel.replace(versionRegex, `version = "${version}"`);
2938
+ } else {
2939
+ topLevel = insertAtTopLevelStart(topLevel, `version = "${version}"`);
2940
+ }
2941
+ const lastUpdatedRegex = /^lastUpdated\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m;
2942
+ const lastUpdatedMatch = topLevel.match(lastUpdatedRegex);
2943
+ if (lastUpdatedMatch) {
2944
+ topLevel = topLevel.replace(lastUpdatedRegex, `lastUpdated = "${lastUpdated}"`);
2945
+ } else {
2946
+ topLevel = insertAfterVersionField(topLevel, `lastUpdated = "${lastUpdated}"`);
2947
+ }
2948
+ if (rest.length > 0 && !topLevel.endsWith("\n\n") && !topLevel.endsWith("\n")) {
2949
+ topLevel += "\n";
2950
+ }
2951
+ return topLevel + rest;
2952
+ }
2874
2953
  function writeTomlConfig(configPath, config) {
2875
2954
  try {
2876
2955
  const configDir = dirname(configPath);
2877
2956
  ensureDir(configDir);
2878
- const tomlContent = stringify(config);
2879
- writeFile(configPath, tomlContent);
2957
+ if (exists(configPath)) {
2958
+ const existingContent = readFile(configPath);
2959
+ const edits = [
2960
+ // General section
2961
+ ["general.preferredLang", config.general.preferredLang],
2962
+ ["general.currentTool", config.general.currentTool]
2963
+ ];
2964
+ if (config.general.templateLang !== void 0) {
2965
+ edits.push(["general.templateLang", config.general.templateLang]);
2966
+ }
2967
+ if (config.general.aiOutputLang !== void 0) {
2968
+ edits.push(["general.aiOutputLang", config.general.aiOutputLang]);
2969
+ }
2970
+ edits.push(
2971
+ ["claudeCode.enabled", config.claudeCode.enabled],
2972
+ ["claudeCode.outputStyles", config.claudeCode.outputStyles],
2973
+ ["claudeCode.installType", config.claudeCode.installType]
2974
+ );
2975
+ if (config.claudeCode.defaultOutputStyle !== void 0) {
2976
+ edits.push(["claudeCode.defaultOutputStyle", config.claudeCode.defaultOutputStyle]);
2977
+ }
2978
+ if (config.claudeCode.currentProfile !== void 0) {
2979
+ edits.push(["claudeCode.currentProfile", config.claudeCode.currentProfile]);
2980
+ }
2981
+ if (config.claudeCode.profiles !== void 0) {
2982
+ edits.push(["claudeCode.profiles", config.claudeCode.profiles]);
2983
+ }
2984
+ if (config.claudeCode.version !== void 0) {
2985
+ edits.push(["claudeCode.version", config.claudeCode.version]);
2986
+ }
2987
+ edits.push(
2988
+ ["codex.enabled", config.codex.enabled],
2989
+ ["codex.systemPromptStyle", config.codex.systemPromptStyle]
2990
+ );
2991
+ try {
2992
+ let updatedContent = batchEditToml(existingContent, edits);
2993
+ updatedContent = updateTopLevelTomlFields(
2994
+ updatedContent,
2995
+ config.version,
2996
+ config.lastUpdated
2997
+ );
2998
+ writeFile(configPath, updatedContent);
2999
+ } catch {
3000
+ const tomlContent = stringifyToml(config);
3001
+ writeFile(configPath, tomlContent);
3002
+ }
3003
+ } else {
3004
+ const tomlContent = stringifyToml(config);
3005
+ writeFile(configPath, tomlContent);
3006
+ }
2880
3007
  } catch {
2881
3008
  }
2882
3009
  }
@@ -3380,6 +3507,147 @@ function applyCodexPlatformCommand(config) {
3380
3507
  }
3381
3508
  }
3382
3509
 
3510
+ function updateTopLevelTomlField(content, field, value, options = {}) {
3511
+ if (!content) {
3512
+ if (value === null) {
3513
+ return "";
3514
+ }
3515
+ const commentPrefix = options.commented ? "# " : "";
3516
+ return `${commentPrefix}${field} = "${value}"
3517
+ `;
3518
+ }
3519
+ const firstSectionMatch = content.match(/^\[/m);
3520
+ const topLevelEnd = firstSectionMatch?.index ?? content.length;
3521
+ let topLevel = content.slice(0, topLevelEnd);
3522
+ const rest = content.slice(topLevelEnd);
3523
+ const fieldRegex = new RegExp(`^(#\\s*)?${field}\\s*=\\s*["'][^"']*["'][ \\t]*(?:#.*)?$`, "m");
3524
+ const existingMatch = topLevel.match(fieldRegex);
3525
+ if (value === null) {
3526
+ if (existingMatch) {
3527
+ topLevel = topLevel.replace(fieldRegex, "").replace(/\n{2,}/g, "\n\n");
3528
+ }
3529
+ } else {
3530
+ const commentPrefix = options.commented ? "# " : "";
3531
+ const newLine = `${commentPrefix}${field} = "${value}"`;
3532
+ if (existingMatch) {
3533
+ topLevel = topLevel.replace(fieldRegex, newLine);
3534
+ } else {
3535
+ topLevel = `${topLevel.trimEnd()}
3536
+ ${newLine}
3537
+ `;
3538
+ }
3539
+ }
3540
+ if (rest.length > 0 && !topLevel.endsWith("\n\n")) {
3541
+ topLevel = `${topLevel.trimEnd()}
3542
+
3543
+ `;
3544
+ }
3545
+ return topLevel + rest;
3546
+ }
3547
+ function updateCodexApiFields(fields) {
3548
+ if (!exists(CODEX_CONFIG_FILE)) {
3549
+ ensureDir(CODEX_DIR);
3550
+ writeFile(CODEX_CONFIG_FILE, "");
3551
+ }
3552
+ let content = readFile(CODEX_CONFIG_FILE) || "";
3553
+ if (fields.model !== void 0) {
3554
+ content = updateTopLevelTomlField(content, "model", fields.model);
3555
+ }
3556
+ if (fields.modelProvider !== void 0) {
3557
+ content = updateTopLevelTomlField(
3558
+ content,
3559
+ "model_provider",
3560
+ fields.modelProvider,
3561
+ { commented: fields.modelProviderCommented }
3562
+ );
3563
+ }
3564
+ writeFile(CODEX_CONFIG_FILE, content);
3565
+ }
3566
+ function upsertCodexProvider(providerId, provider) {
3567
+ if (!exists(CODEX_CONFIG_FILE)) {
3568
+ ensureDir(CODEX_DIR);
3569
+ writeFile(CODEX_CONFIG_FILE, "");
3570
+ }
3571
+ let content = readFile(CODEX_CONFIG_FILE) || "";
3572
+ const basePath = `model_providers.${providerId}`;
3573
+ content = editToml(content, `${basePath}.name`, provider.name);
3574
+ content = editToml(content, `${basePath}.base_url`, provider.baseUrl);
3575
+ content = editToml(content, `${basePath}.wire_api`, provider.wireApi);
3576
+ content = editToml(content, `${basePath}.temp_env_key`, provider.tempEnvKey);
3577
+ content = editToml(content, `${basePath}.requires_openai_auth`, provider.requiresOpenaiAuth);
3578
+ if (provider.model) {
3579
+ content = editToml(content, `${basePath}.model`, provider.model);
3580
+ }
3581
+ writeFile(CODEX_CONFIG_FILE, content);
3582
+ }
3583
+ function deleteCodexProvider(providerId) {
3584
+ if (!exists(CODEX_CONFIG_FILE)) {
3585
+ return;
3586
+ }
3587
+ const content = readFile(CODEX_CONFIG_FILE) || "";
3588
+ const sectionRegex = new RegExp(
3589
+ `\\n?\\[model_providers\\.${escapeRegex(providerId)}\\][\\s\\S]*?(?=\\n\\[|$)`,
3590
+ "g"
3591
+ );
3592
+ const updatedContent = content.replace(sectionRegex, "");
3593
+ writeFile(CODEX_CONFIG_FILE, updatedContent);
3594
+ }
3595
+ function upsertCodexMcpService(serviceId, service) {
3596
+ if (!exists(CODEX_CONFIG_FILE)) {
3597
+ ensureDir(CODEX_DIR);
3598
+ writeFile(CODEX_CONFIG_FILE, "");
3599
+ }
3600
+ let content = readFile(CODEX_CONFIG_FILE) || "";
3601
+ const basePath = `mcp_servers.${serviceId}`;
3602
+ const parsed = content ? parseToml(content) : {};
3603
+ const existingService = parsed.mcp_servers?.[serviceId];
3604
+ if (existingService?.url && !existingService?.command) {
3605
+ if (service.env && Object.keys(service.env).length > 0) {
3606
+ content = editToml(content, `${basePath}.env`, service.env);
3607
+ }
3608
+ if (service.startup_timeout_sec) {
3609
+ content = editToml(content, `${basePath}.startup_timeout_sec`, service.startup_timeout_sec);
3610
+ }
3611
+ } else {
3612
+ const normalizedCommand = normalizeTomlPath(service.command);
3613
+ content = editToml(content, `${basePath}.command`, normalizedCommand);
3614
+ content = editToml(content, `${basePath}.args`, service.args || []);
3615
+ if (service.env && Object.keys(service.env).length > 0) {
3616
+ content = editToml(content, `${basePath}.env`, service.env);
3617
+ }
3618
+ if (service.startup_timeout_sec) {
3619
+ content = editToml(content, `${basePath}.startup_timeout_sec`, service.startup_timeout_sec);
3620
+ }
3621
+ }
3622
+ writeFile(CODEX_CONFIG_FILE, content);
3623
+ }
3624
+ function batchUpdateCodexMcpServices(services, options = {}) {
3625
+ if (options.replaceAll) {
3626
+ if (exists(CODEX_CONFIG_FILE)) {
3627
+ let content = readFile(CODEX_CONFIG_FILE) || "";
3628
+ content = content.replace(/\n?\[mcp_servers\.[^\]]+\][\s\S]*?(?=\n\[|$)/g, "");
3629
+ content = content.replace(/\n?#\s*---\s*MCP servers added by ZCF\s*---\s*/gi, "");
3630
+ writeFile(CODEX_CONFIG_FILE, content);
3631
+ }
3632
+ }
3633
+ for (const service of services) {
3634
+ upsertCodexMcpService(service.id, service);
3635
+ }
3636
+ }
3637
+ function escapeRegex(str) {
3638
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3639
+ }
3640
+
3641
+ const codexTomlUpdater = {
3642
+ __proto__: null,
3643
+ batchUpdateCodexMcpServices: batchUpdateCodexMcpServices,
3644
+ deleteCodexProvider: deleteCodexProvider,
3645
+ updateCodexApiFields: updateCodexApiFields,
3646
+ updateTopLevelTomlField: updateTopLevelTomlField,
3647
+ upsertCodexMcpService: upsertCodexMcpService,
3648
+ upsertCodexProvider: upsertCodexProvider
3649
+ };
3650
+
3383
3651
  async function configureCodexMcp(options) {
3384
3652
  ensureI18nInitialized();
3385
3653
  const { skipPrompt = false } = options ?? {};
@@ -3396,7 +3664,6 @@ async function configureCodexMcp(options) {
3396
3664
  return;
3397
3665
  }
3398
3666
  const defaultServiceIds = Array.isArray(options?.mcpServices) ? options.mcpServices : MCP_SERVICE_CONFIGS.filter((service) => !service.requiresApiKey).map((service) => service.id);
3399
- const baseProviders2 = existingConfig?.providers || [];
3400
3667
  const existingServices2 = existingConfig?.mcpServices || [];
3401
3668
  const selection2 = [];
3402
3669
  for (const id of defaultServiceIds) {
@@ -3450,13 +3717,7 @@ async function configureCodexMcp(options) {
3450
3717
  }
3451
3718
  return svc;
3452
3719
  });
3453
- writeCodexConfig({
3454
- model: existingConfig?.model || null,
3455
- modelProvider: existingConfig?.modelProvider || null,
3456
- providers: baseProviders2,
3457
- mcpServices: finalServices2,
3458
- otherConfig: existingConfig?.otherConfig || []
3459
- });
3720
+ batchUpdateCodexMcpServices(finalServices2);
3460
3721
  updateZcfConfig({ codeToolType: "codex" });
3461
3722
  console.log(ansis.green(i18n.t("codex:mcpConfigured")));
3462
3723
  return;
@@ -3465,7 +3726,6 @@ async function configureCodexMcp(options) {
3465
3726
  if (!selectedIds)
3466
3727
  return;
3467
3728
  const servicesMeta = await getMcpServices();
3468
- const baseProviders = existingConfig?.providers || [];
3469
3729
  const selection = [];
3470
3730
  const existingServices = existingConfig?.mcpServices || [];
3471
3731
  if (selectedIds.length === 0) {
@@ -3485,13 +3745,7 @@ async function configureCodexMcp(options) {
3485
3745
  }
3486
3746
  return svc;
3487
3747
  });
3488
- writeCodexConfig({
3489
- model: existingConfig?.model || null,
3490
- modelProvider: existingConfig?.modelProvider || null,
3491
- providers: baseProviders,
3492
- mcpServices: preserved,
3493
- otherConfig: existingConfig?.otherConfig || []
3494
- });
3748
+ batchUpdateCodexMcpServices(preserved);
3495
3749
  updateZcfConfig({ codeToolType: "codex" });
3496
3750
  return;
3497
3751
  }
@@ -3560,13 +3814,7 @@ async function configureCodexMcp(options) {
3560
3814
  }
3561
3815
  return svc;
3562
3816
  });
3563
- writeCodexConfig({
3564
- model: existingConfig?.model || null,
3565
- modelProvider: existingConfig?.modelProvider || null,
3566
- providers: baseProviders,
3567
- mcpServices: finalServices,
3568
- otherConfig: existingConfig?.otherConfig || []
3569
- });
3817
+ batchUpdateCodexMcpServices(finalServices);
3570
3818
  updateZcfConfig({ codeToolType: "codex" });
3571
3819
  console.log(ansis.green(i18n.t("codex:mcpConfigured")));
3572
3820
  }
@@ -3828,7 +4076,7 @@ function parseCodexConfig(content) {
3828
4076
  const normalizedContent = content.replace(/(SYSTEMROOT\s*=\s*")[^"\n]+("?)/g, (match) => {
3829
4077
  return match.replace(/\\\\/g, "/").replace(/\\/g, "/").replace('C:/Windows"?', 'C:/Windows"');
3830
4078
  });
3831
- const tomlData = parse(normalizedContent);
4079
+ const tomlData = parseToml(normalizedContent);
3832
4080
  const providers = [];
3833
4081
  if (tomlData.model_providers) {
3834
4082
  for (const [id, providerData] of Object.entries(tomlData.model_providers)) {
@@ -3962,70 +4210,6 @@ function parseCodexConfig(content) {
3962
4210
  };
3963
4211
  }
3964
4212
  }
3965
- function formatInlineTableValue(value) {
3966
- if (value === null || value === void 0) {
3967
- return "";
3968
- }
3969
- if (typeof value === "string") {
3970
- const normalized = normalizeTomlPath(value);
3971
- return `'${normalized}'`;
3972
- }
3973
- if (typeof value === "number" || typeof value === "boolean") {
3974
- return String(value);
3975
- }
3976
- if (Array.isArray(value)) {
3977
- const items = value.map((item) => {
3978
- if (typeof item === "string") {
3979
- const normalized = normalizeTomlPath(item);
3980
- return `'${normalized}'`;
3981
- }
3982
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
3983
- return formatInlineTable(item);
3984
- }
3985
- return String(item);
3986
- }).join(", ");
3987
- return `[${items}]`;
3988
- }
3989
- if (typeof value === "object") {
3990
- return formatInlineTable(value);
3991
- }
3992
- return String(value);
3993
- }
3994
- function formatInlineTable(obj) {
3995
- const entries = Object.entries(obj).filter(([_, v]) => v !== null && v !== void 0).map(([k, v]) => `${k} = ${formatInlineTableValue(v)}`).join(", ");
3996
- return `{${entries}}`;
3997
- }
3998
- function formatTomlField(key, value) {
3999
- if (value === null || value === void 0) {
4000
- return "";
4001
- }
4002
- if (typeof value === "string") {
4003
- const normalized = normalizeTomlPath(value);
4004
- const escaped = normalized.replace(/"/g, '\\"');
4005
- return `${key} = "${escaped}"`;
4006
- }
4007
- if (typeof value === "number" || typeof value === "boolean") {
4008
- return `${key} = ${value}`;
4009
- }
4010
- if (Array.isArray(value)) {
4011
- const items = value.map((item) => {
4012
- if (typeof item === "string") {
4013
- const normalized = normalizeTomlPath(item);
4014
- const escaped = normalized.replace(/"/g, '\\"');
4015
- return `"${escaped}"`;
4016
- }
4017
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
4018
- return formatInlineTable(item);
4019
- }
4020
- return String(item);
4021
- }).join(", ");
4022
- return `${key} = [${items}]`;
4023
- }
4024
- if (typeof value === "object") {
4025
- return `${key} = ${formatInlineTable(value)}`;
4026
- }
4027
- return "";
4028
- }
4029
4213
  function readCodexConfig() {
4030
4214
  if (!exists(CODEX_CONFIG_FILE))
4031
4215
  return null;
@@ -4037,100 +4221,6 @@ function readCodexConfig() {
4037
4221
  return null;
4038
4222
  }
4039
4223
  }
4040
- function renderCodexConfig(data) {
4041
- const lines = [];
4042
- if (data.model || data.modelProvider || data.providers.length > 0 || data.modelProviderCommented) {
4043
- lines.push("# --- model provider added by ZCF ---");
4044
- if (data.model) {
4045
- lines.push(`model = "${data.model}"`);
4046
- }
4047
- if (data.modelProvider) {
4048
- const commentPrefix = data.modelProviderCommented ? "# " : "";
4049
- lines.push(`${commentPrefix}model_provider = "${data.modelProvider}"`);
4050
- }
4051
- lines.push("");
4052
- }
4053
- if (data.otherConfig && data.otherConfig.length > 0) {
4054
- const preserved = data.otherConfig.filter((raw) => {
4055
- const l = String(raw).trim();
4056
- if (!l)
4057
- return false;
4058
- if (/^#\s*---\s*model provider added by ZCF\s*---\s*$/i.test(l))
4059
- return false;
4060
- if (/^#\s*---\s*MCP servers added by ZCF\s*---\s*$/i.test(l))
4061
- return false;
4062
- if (/^\[\s*mcp_servers\./i.test(l))
4063
- return false;
4064
- if (/^\[\s*model_providers\./i.test(l))
4065
- return false;
4066
- if (/^#?\s*model_provider\s*=/.test(l))
4067
- return false;
4068
- if (/^\s*model\s*=/.test(l) && !l.includes("["))
4069
- return false;
4070
- return true;
4071
- });
4072
- if (preserved.length > 0) {
4073
- lines.push(...preserved);
4074
- if (data.providers.length > 0 || data.mcpServices.length > 0) {
4075
- lines.push("");
4076
- }
4077
- }
4078
- }
4079
- if (data.providers.length > 0) {
4080
- for (const provider of data.providers) {
4081
- lines.push("");
4082
- lines.push(`[model_providers.${provider.id}]`);
4083
- lines.push(`name = "${provider.name}"`);
4084
- lines.push(`base_url = "${provider.baseUrl}"`);
4085
- lines.push(`wire_api = "${provider.wireApi}"`);
4086
- lines.push(`temp_env_key = "${provider.tempEnvKey}"`);
4087
- lines.push(`requires_openai_auth = ${provider.requiresOpenaiAuth}`);
4088
- if (provider.model) {
4089
- lines.push(`model = "${provider.model}"`);
4090
- }
4091
- }
4092
- }
4093
- if (data.mcpServices.length > 0) {
4094
- lines.push("");
4095
- lines.push("# --- MCP servers added by ZCF ---");
4096
- for (const service of data.mcpServices) {
4097
- lines.push(`[mcp_servers.${service.id}]`);
4098
- const normalizedCommand = normalizeTomlPath(service.command);
4099
- lines.push(`command = "${normalizedCommand}"`);
4100
- const argsString = service.args.length > 0 ? service.args.map((arg) => `"${arg}"`).join(", ") : "";
4101
- lines.push(`args = [${argsString}]`);
4102
- if (service.env && Object.keys(service.env).length > 0) {
4103
- const envEntries = Object.entries(service.env).map(([key, value]) => `${key} = '${value}'`).join(", ");
4104
- lines.push(`env = {${envEntries}}`);
4105
- }
4106
- if (service.startup_timeout_sec) {
4107
- lines.push(`startup_timeout_sec = ${service.startup_timeout_sec}`);
4108
- }
4109
- if (service.extraFields) {
4110
- for (const [key, value] of Object.entries(service.extraFields)) {
4111
- const formatted = formatTomlField(key, value);
4112
- if (formatted) {
4113
- lines.push(formatted);
4114
- }
4115
- }
4116
- }
4117
- lines.push("");
4118
- }
4119
- if (lines[lines.length - 1] === "") {
4120
- lines.pop();
4121
- }
4122
- }
4123
- let result = lines.join("\n");
4124
- if (result && !result.endsWith("\n")) {
4125
- result += "\n";
4126
- }
4127
- return result;
4128
- }
4129
- function writeCodexConfig(data) {
4130
- ensureEnvKeyMigration();
4131
- ensureDir(CODEX_DIR);
4132
- writeFile(CODEX_CONFIG_FILE, renderCodexConfig(data));
4133
- }
4134
4224
  function writeAuthFile(newEntries) {
4135
4225
  ensureDir(CODEX_DIR);
4136
4226
  const existing = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {};
@@ -4473,12 +4563,11 @@ async function applyCustomApiConfig(customApiConfig) {
4473
4563
  }
4474
4564
  const existingConfig = readCodexConfig();
4475
4565
  const existingAuth = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {};
4476
- const providers = [];
4477
4566
  const authEntries = { ...existingAuth };
4478
4567
  const providerId = type === "auth_token" ? "official-auth-token" : "custom-api-key";
4479
4568
  const providerName = type === "auth_token" ? "Official Auth Token" : "Custom API Key";
4480
4569
  const existingProvider = existingConfig?.providers.find((p) => p.id === providerId);
4481
- providers.push({
4570
+ const newProvider = {
4482
4571
  id: providerId,
4483
4572
  name: providerName,
4484
4573
  baseUrl: baseUrl || existingProvider?.baseUrl || "https://api.anthropic.com",
@@ -4486,24 +4575,18 @@ async function applyCustomApiConfig(customApiConfig) {
4486
4575
  tempEnvKey: existingProvider?.tempEnvKey || `${providerId.toUpperCase()}_API_KEY`,
4487
4576
  requiresOpenaiAuth: existingProvider?.requiresOpenaiAuth ?? false,
4488
4577
  model: model || existingProvider?.model
4489
- });
4490
- if (existingConfig?.providers) {
4491
- providers.push(...existingConfig.providers.filter((p) => p.id !== providerId));
4492
- }
4578
+ };
4493
4579
  if (token) {
4494
4580
  authEntries[providerId] = token;
4495
4581
  authEntries.OPENAI_API_KEY = token;
4496
4582
  }
4497
- const configData = {
4583
+ const { updateCodexApiFields, upsertCodexProvider } = await Promise.resolve().then(function () { return codexTomlUpdater; });
4584
+ updateCodexApiFields({
4498
4585
  model: model || existingConfig?.model || "claude-3-5-sonnet-20241022",
4499
- // Prefer provided model, then existing, fallback default
4500
4586
  modelProvider: providerId,
4501
- modelProviderCommented: false,
4502
- providers,
4503
- mcpServices: existingConfig?.mcpServices || [],
4504
- otherConfig: existingConfig?.otherConfig || []
4505
- };
4506
- writeCodexConfig(configData);
4587
+ modelProviderCommented: false
4588
+ });
4589
+ upsertCodexProvider(providerId, newProvider);
4507
4590
  writeJsonConfig(CODEX_AUTH_FILE, authEntries);
4508
4591
  updateZcfConfig({ codeToolType: "codex" });
4509
4592
  console.log(ansis.green(`\u2714 ${i18n.t("codex:apiConfigured")}`));
@@ -4636,7 +4719,8 @@ async function configureCodexApi(options) {
4636
4719
  const sanitized = sanitizeProviderName(input);
4637
4720
  if (!sanitized)
4638
4721
  return i18n.t("codex:providerNameRequired");
4639
- if (sanitized !== input.trim())
4722
+ const normalizedInput = input.trim().toLowerCase().replace(/\./g, "-").replace(/\s+/g, "-");
4723
+ if (sanitized !== normalizedInput)
4640
4724
  return i18n.t("codex:providerNameInvalid");
4641
4725
  return true;
4642
4726
  }
@@ -4674,7 +4758,7 @@ async function configureCodexApi(options) {
4674
4758
  type: "input",
4675
4759
  name: "model",
4676
4760
  message: `${i18n.t("configuration:enterCustomModel")}${i18n.t("common:emptyToSkip")}`,
4677
- default: "gpt-5-codex"
4761
+ default: "gpt-5.2"
4678
4762
  }]);
4679
4763
  if (model.trim()) {
4680
4764
  customModel = model.trim();
@@ -4713,7 +4797,7 @@ async function configureCodexApi(options) {
4713
4797
  wireApi: selectedProvider2 === "custom" ? answers.wireApi || "responses" : prefilledWireApi,
4714
4798
  tempEnvKey,
4715
4799
  requiresOpenaiAuth: true,
4716
- model: customModel || prefilledModel || "gpt-5-codex"
4800
+ model: customModel || prefilledModel || "gpt-5.2"
4717
4801
  // Use custom model, provider's default model, or fallback
4718
4802
  };
4719
4803
  providers.push(newProvider);
@@ -4743,13 +4827,15 @@ async function configureCodexApi(options) {
4743
4827
  if (defaultApiKey)
4744
4828
  authEntries.OPENAI_API_KEY = defaultApiKey;
4745
4829
  }
4746
- writeCodexConfig({
4747
- model: existingConfig?.model || null,
4830
+ const { updateCodexApiFields, upsertCodexProvider } = await Promise.resolve().then(function () { return codexTomlUpdater; });
4831
+ updateCodexApiFields({
4832
+ model: existingConfig?.model,
4748
4833
  modelProvider: defaultProvider,
4749
- providers,
4750
- mcpServices: existingConfig?.mcpServices || [],
4751
- otherConfig: existingConfig?.otherConfig || []
4834
+ modelProviderCommented: false
4752
4835
  });
4836
+ for (const provider of providers) {
4837
+ upsertCodexProvider(provider.id, provider);
4838
+ }
4753
4839
  writeAuthFile(authEntries);
4754
4840
  updateZcfConfig({ codeToolType: "codex" });
4755
4841
  console.log(ansis.green(i18n.t("codex:apiConfigured")));
@@ -4937,12 +5023,12 @@ async function switchCodexProvider(providerId) {
4937
5023
  if (backupPath) {
4938
5024
  console.log(ansis.gray(getBackupMessage(backupPath)));
4939
5025
  }
4940
- const updatedConfig = {
4941
- ...existingConfig,
4942
- modelProvider: providerId
4943
- };
4944
5026
  try {
4945
- writeCodexConfig(updatedConfig);
5027
+ const { updateCodexApiFields } = await Promise.resolve().then(function () { return codexTomlUpdater; });
5028
+ updateCodexApiFields({
5029
+ modelProvider: providerId,
5030
+ modelProviderCommented: false
5031
+ });
4946
5032
  console.log(ansis.green(i18n.t("codex:providerSwitchSuccess", { provider: providerId })));
4947
5033
  return true;
4948
5034
  } catch (error) {
@@ -4966,19 +5052,18 @@ async function switchToOfficialLogin() {
4966
5052
  if (!preservedModelProvider) {
4967
5053
  try {
4968
5054
  const rawContent = readFile(CODEX_CONFIG_FILE);
4969
- const parsedToml = parse(rawContent);
5055
+ const parsedToml = parseToml(rawContent);
4970
5056
  if (typeof parsedToml.model_provider === "string" && parsedToml.model_provider.trim().length > 0)
4971
5057
  preservedModelProvider = parsedToml.model_provider.trim();
4972
5058
  } catch {
4973
5059
  }
4974
5060
  }
4975
5061
  const shouldCommentModelProvider = typeof preservedModelProvider === "string" && preservedModelProvider.length > 0;
4976
- const updatedConfig = {
4977
- ...existingConfig,
5062
+ const { updateCodexApiFields } = await Promise.resolve().then(function () { return codexTomlUpdater; });
5063
+ updateCodexApiFields({
4978
5064
  modelProvider: shouldCommentModelProvider ? preservedModelProvider : existingConfig.modelProvider,
4979
- modelProviderCommented: shouldCommentModelProvider ? true : existingConfig.modelProviderCommented
4980
- };
4981
- writeCodexConfig(updatedConfig);
5065
+ modelProviderCommented: shouldCommentModelProvider || existingConfig.modelProviderCommented
5066
+ });
4982
5067
  const auth = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {};
4983
5068
  auth.OPENAI_API_KEY = null;
4984
5069
  writeJsonConfig(CODEX_AUTH_FILE, auth, { pretty: true });
@@ -5011,18 +5096,18 @@ async function switchToProvider(providerId) {
5011
5096
  targetModel = provider.model;
5012
5097
  } else {
5013
5098
  const currentModel = existingConfig.model;
5014
- if (currentModel !== "gpt-5" && currentModel !== "gpt-5-codex") {
5015
- targetModel = "gpt-5-codex";
5099
+ const knownModels = ["gpt-5.1-codex-max", "gpt-5.1-codex-mini", "gpt-5.2"];
5100
+ if (!currentModel || !knownModels.includes(currentModel)) {
5101
+ targetModel = "gpt-5.2";
5016
5102
  }
5017
5103
  }
5018
- const updatedConfig = {
5019
- ...existingConfig,
5104
+ const { updateCodexApiFields } = await Promise.resolve().then(function () { return codexTomlUpdater; });
5105
+ updateCodexApiFields({
5020
5106
  model: targetModel,
5021
5107
  modelProvider: providerId,
5022
5108
  modelProviderCommented: false
5023
5109
  // Ensure it's not commented
5024
- };
5025
- writeCodexConfig(updatedConfig);
5110
+ });
5026
5111
  const auth = readJsonConfig(CODEX_AUTH_FILE, { defaultValue: {} }) || {};
5027
5112
  const envValue = auth[provider.tempEnvKey] || null;
5028
5113
  auth.OPENAI_API_KEY = envValue;
@@ -5059,7 +5144,6 @@ const codex = {
5059
5144
  needsEnvKeyMigration: needsEnvKeyMigration,
5060
5145
  parseCodexConfig: parseCodexConfig,
5061
5146
  readCodexConfig: readCodexConfig,
5062
- renderCodexConfig: renderCodexConfig,
5063
5147
  runCodexFullInit: runCodexFullInit,
5064
5148
  runCodexSystemPromptSelection: runCodexSystemPromptSelection,
5065
5149
  runCodexUninstall: runCodexUninstall,
@@ -5069,8 +5153,7 @@ const codex = {
5069
5153
  switchCodexProvider: switchCodexProvider,
5070
5154
  switchToOfficialLogin: switchToOfficialLogin,
5071
5155
  switchToProvider: switchToProvider,
5072
- writeAuthFile: writeAuthFile,
5073
- writeCodexConfig: writeCodexConfig
5156
+ writeAuthFile: writeAuthFile
5074
5157
  };
5075
5158
 
5076
5159
  const CODE_TYPE_ABBREVIATIONS = {
@@ -5239,6 +5322,16 @@ const OUTPUT_STYLES = [
5239
5322
  isCustom: true,
5240
5323
  filePath: "ojousama-engineer.md"
5241
5324
  },
5325
+ {
5326
+ id: "rem-engineer",
5327
+ isCustom: true,
5328
+ filePath: "rem-engineer.md"
5329
+ },
5330
+ {
5331
+ id: "leibus-engineer",
5332
+ isCustom: true,
5333
+ filePath: "leibus-engineer.md"
5334
+ },
5242
5335
  // Built-in styles (no template files)
5243
5336
  {
5244
5337
  id: "default",
@@ -5332,6 +5425,16 @@ async function configureOutputStyle(preselectedStyles, preselectedDefault) {
5332
5425
  id: "ojousama-engineer",
5333
5426
  name: i18n.t("configuration:outputStyles.ojousama-engineer.name"),
5334
5427
  description: i18n.t("configuration:outputStyles.ojousama-engineer.description")
5428
+ },
5429
+ {
5430
+ id: "leibus-engineer",
5431
+ name: i18n.t("configuration:outputStyles.leibus-engineer.name"),
5432
+ description: i18n.t("configuration:outputStyles.leibus-engineer.description")
5433
+ },
5434
+ {
5435
+ id: "rem-engineer",
5436
+ name: i18n.t("configuration:outputStyles.rem-engineer.name"),
5437
+ description: i18n.t("configuration:outputStyles.rem-engineer.description")
5335
5438
  }
5336
5439
  ];
5337
5440
  const availableStyles = getAvailableOutputStyles();
@@ -7488,7 +7591,7 @@ async function convertToCodexProvider(config) {
7488
7591
  const displayName = config.name || config.provider || "custom";
7489
7592
  const providerId = displayName.toLowerCase().replace(/[^a-z0-9]/g, "-");
7490
7593
  let baseUrl = config.url || API_DEFAULT_URL;
7491
- let model = config.primaryModel || "gpt-5-codex";
7594
+ let model = config.primaryModel || "gpt-5.2";
7492
7595
  let wireApi = "responses";
7493
7596
  if (config.provider && config.provider !== "custom") {
7494
7597
  const { getProviderPreset } = await import('./api-providers.mjs');
@@ -7588,4 +7691,4 @@ async function openSettingsJson() {
7588
7691
  }
7589
7692
  }
7590
7693
 
7591
- export { getExistingModelConfig as $, API_DEFAULT_URL as A, getAiOutputLanguageLabel as B, CLAUDE_DIR as C, DEFAULT_CODE_TOOL_TYPE as D, getMcpConfigPath as E, readMcpConfig as F, writeMcpConfig as G, backupMcpConfig as H, mergeMcpServers as I, buildMcpServerConfig as J, fixWindowsMcpConfig as K, LEGACY_ZCF_CONFIG_FILES as L, addCompletedOnboarding as M, ensureApiKeyApproved as N, removeApiKeyFromRejected as O, manageApiKeyApproval as P, setPrimaryApiKey as Q, ensureClaudeDir as R, SETTINGS_FILE as S, backupExistingConfig as T, copyConfigFiles as U, configureApi as V, mergeConfigs as W, updateCustomModel as X, updateDefaultModel as Y, ZCF_CONFIG_DIR as Z, mergeSettingsFile as _, commandExists as a, resolveAiOutputLanguage as a$, getExistingApiConfig as a0, applyAiLanguageDirective as a1, switchToOfficialLogin$1 as a2, promptApiConfigurationAction as a3, isClaudeCodeInstalled as a4, installClaudeCode as a5, isCodexInstalled as a6, installCodex as a7, isLocalClaudeCodeInstalled as a8, getInstallationStatus as a9, writeCodexConfig as aA, writeAuthFile as aB, updateZcfConfig as aC, changeLanguage as aD, readZcfConfig as aE, configureOutputStyle as aF, isWindows as aG, selectMcpServices as aH, getMcpServices as aI, isCcrInstalled as aJ, installCcr as aK, setupCcrConfiguration as aL, modifyApiConfigPartially as aM, formatApiKeyDisplay as aN, readCcrConfig as aO, configureCcrFeature as aP, handleExitPromptError as aQ, handleGeneralError as aR, COMETIX_COMMAND_NAME as aS, COMETIX_COMMANDS as aT, installCometixLine as aU, checkAndUpdateTools as aV, runCodexUpdate as aW, resolveCodeType as aX, writeJsonConfig as aY, displayBanner as aZ, version as a_, removeLocalClaudeCode as aa, uninstallCodeTool as ab, setInstallMethod as ac, detectInstalledVersion as ad, selectInstallMethod as ae, executeInstallMethod as af, handleInstallFailure as ag, verifyInstallation as ah, createHomebrewSymlink as ai, displayVerificationResult as aj, ensureI18nInitialized as ak, i18n as al, addNumbersToChoices as am, validateApiKey as an, promptBoolean as ao, ensureDir as ap, readDefaultTomlConfig as aq, createDefaultTomlConfig as ar, exists as as, readJsonConfig as at, writeTomlConfig as au, clearModelEnv as av, copyFile as aw, detectConfigManagementMode as ax, readCodexConfig as ay, backupCodexComplete as az, importRecommendedEnv as b, updatePromptOnly as b0, selectAndInstallWorkflows as b1, checkClaudeCodeVersionAndPrompt as b2, displayBannerWithInfo as b3, runCodexUninstall as b4, configureCodexMcp as b5, configureCodexApi as b6, runCodexWorkflowImportWithLanguageSelection as b7, runCodexFullInit as b8, switchCodexProvider as b9, listCodexProviders as ba, switchToOfficialLogin as bb, switchToProvider as bc, readZcfConfigAsync as bd, initI18n as be, selectScriptLanguage as bf, index as bg, fsOperations as bh, jsonConfig as bi, claudeConfig as bj, config$1 as bk, config as bl, prompts as bm, codex as bn, installer as bo, cleanupPermissions as c, importRecommendedPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, CLAUDE_VSC_CONFIG_FILE as h, init as i, CODEX_DIR as j, CODEX_CONFIG_FILE as k, CODEX_AUTH_FILE as l, mergeAndCleanPermissions as m, CODEX_AGENTS_FILE as n, openSettingsJson as o, CODEX_PROMPTS_DIR as p, ZCF_CONFIG_FILE as q, CODE_TOOL_TYPES as r, CODE_TOOL_BANNERS as s, CODE_TOOL_ALIASES as t, isCodeToolType as u, API_ENV_KEY as v, resolveCodeToolType as w, SUPPORTED_LANGS as x, LANG_LABELS as y, AI_OUTPUT_LANGUAGES as z };
7694
+ export { getExistingModelConfig as $, API_DEFAULT_URL as A, getAiOutputLanguageLabel as B, CLAUDE_DIR as C, DEFAULT_CODE_TOOL_TYPE as D, getMcpConfigPath as E, readMcpConfig as F, writeMcpConfig as G, backupMcpConfig as H, mergeMcpServers as I, buildMcpServerConfig as J, fixWindowsMcpConfig as K, LEGACY_ZCF_CONFIG_FILES as L, addCompletedOnboarding as M, ensureApiKeyApproved as N, removeApiKeyFromRejected as O, manageApiKeyApproval as P, setPrimaryApiKey as Q, ensureClaudeDir as R, SETTINGS_FILE as S, backupExistingConfig as T, copyConfigFiles as U, configureApi as V, mergeConfigs as W, updateCustomModel as X, updateDefaultModel as Y, ZCF_CONFIG_DIR as Z, mergeSettingsFile as _, commandExists as a, updatePromptOnly as a$, getExistingApiConfig as a0, applyAiLanguageDirective as a1, switchToOfficialLogin$1 as a2, promptApiConfigurationAction as a3, isClaudeCodeInstalled as a4, installClaudeCode as a5, isCodexInstalled as a6, installCodex as a7, isLocalClaudeCodeInstalled as a8, getInstallationStatus as a9, writeAuthFile as aA, updateZcfConfig as aB, changeLanguage as aC, readZcfConfig as aD, configureOutputStyle as aE, isWindows as aF, selectMcpServices as aG, getMcpServices as aH, isCcrInstalled as aI, installCcr as aJ, setupCcrConfiguration as aK, modifyApiConfigPartially as aL, formatApiKeyDisplay as aM, readCcrConfig as aN, configureCcrFeature as aO, handleExitPromptError as aP, handleGeneralError as aQ, COMETIX_COMMAND_NAME as aR, COMETIX_COMMANDS as aS, installCometixLine as aT, checkAndUpdateTools as aU, runCodexUpdate as aV, resolveCodeType as aW, writeJsonConfig as aX, displayBanner as aY, version as aZ, resolveAiOutputLanguage as a_, removeLocalClaudeCode as aa, uninstallCodeTool as ab, setInstallMethod as ac, detectInstalledVersion as ad, selectInstallMethod as ae, executeInstallMethod as af, handleInstallFailure as ag, verifyInstallation as ah, createHomebrewSymlink as ai, displayVerificationResult as aj, ensureI18nInitialized as ak, i18n as al, addNumbersToChoices as am, validateApiKey as an, promptBoolean as ao, ensureDir as ap, readDefaultTomlConfig as aq, createDefaultTomlConfig as ar, exists as as, readJsonConfig as at, writeTomlConfig as au, clearModelEnv as av, copyFile as aw, detectConfigManagementMode as ax, readCodexConfig as ay, backupCodexComplete as az, importRecommendedEnv as b, selectAndInstallWorkflows as b0, checkClaudeCodeVersionAndPrompt as b1, displayBannerWithInfo as b2, runCodexUninstall as b3, configureCodexMcp as b4, configureCodexApi as b5, runCodexWorkflowImportWithLanguageSelection as b6, runCodexFullInit as b7, switchCodexProvider as b8, listCodexProviders as b9, switchToOfficialLogin as ba, switchToProvider as bb, readZcfConfigAsync as bc, initI18n as bd, selectScriptLanguage as be, index as bf, fsOperations as bg, jsonConfig as bh, claudeConfig as bi, config$1 as bj, config as bk, prompts as bl, codexTomlUpdater as bm, codex as bn, installer as bo, cleanupPermissions as c, importRecommendedPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, CLAUDE_VSC_CONFIG_FILE as h, init as i, CODEX_DIR as j, CODEX_CONFIG_FILE as k, CODEX_AUTH_FILE as l, mergeAndCleanPermissions as m, CODEX_AGENTS_FILE as n, openSettingsJson as o, CODEX_PROMPTS_DIR as p, ZCF_CONFIG_FILE as q, CODE_TOOL_TYPES as r, CODE_TOOL_BANNERS as s, CODE_TOOL_ALIASES as t, isCodeToolType as u, API_ENV_KEY as v, resolveCodeToolType as w, SUPPORTED_LANGS as x, LANG_LABELS as y, AI_OUTPUT_LANGUAGES as z };