zcf 3.1.1 → 3.1.3

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.
@@ -16,7 +16,7 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
16
16
  import i18next from 'i18next';
17
17
  import Backend from 'i18next-fs-backend';
18
18
 
19
- const version = "3.1.1";
19
+ const version = "3.1.3";
20
20
  const homepage = "https://github.com/UfoMiao/zcf";
21
21
 
22
22
  const i18n = i18next.createInstance();
@@ -542,6 +542,17 @@ function getMcpCommand() {
542
542
  }
543
543
  return ["npx"];
544
544
  }
545
+ function getSystemRoot() {
546
+ if (!isWindows())
547
+ return null;
548
+ const env = process.env;
549
+ let systemRoot = "C:\\Windows";
550
+ if (Object.prototype.hasOwnProperty.call(env, "SYSTEMROOT") && env.SYSTEMROOT)
551
+ systemRoot = env.SYSTEMROOT;
552
+ else if (Object.prototype.hasOwnProperty.call(env, "SystemRoot") && env.SystemRoot)
553
+ systemRoot = env.SystemRoot;
554
+ return systemRoot.replace(/\\+/g, "/").replace(/\/+/g, "/");
555
+ }
545
556
  async function commandExists(command) {
546
557
  try {
547
558
  const cmd = getPlatform() === "windows" ? "where" : "which";
@@ -2661,6 +2672,13 @@ function detectConfigManagementMode() {
2661
2672
  }
2662
2673
  }
2663
2674
 
2675
+ function applyCodexPlatformCommand(config) {
2676
+ if (config.command === "npx" && isWindows()) {
2677
+ const mcpCmd = getMcpCommand();
2678
+ config.command = mcpCmd[0];
2679
+ config.args = [...mcpCmd.slice(1), ...config.args || []];
2680
+ }
2681
+ }
2664
2682
  const CODEX_DIR = join(homedir(), ".codex");
2665
2683
  const CODEX_CONFIG_FILE = join(CODEX_DIR, "config.toml");
2666
2684
  const CODEX_AUTH_FILE = join(CODEX_DIR, "auth.json");
@@ -2793,7 +2811,10 @@ function parseCodexConfig(content) {
2793
2811
  };
2794
2812
  }
2795
2813
  try {
2796
- const tomlData = parse(content);
2814
+ const normalizedContent = content.replace(/(SYSTEMROOT\s*=\s*")[^"\n]+("?)/g, (match) => {
2815
+ return match.replace(/\\\\/g, "/").replace(/\\/g, "/").replace('C:/Windows"?', 'C:/Windows"');
2816
+ });
2817
+ const tomlData = parse(normalizedContent);
2797
2818
  const providers = [];
2798
2819
  if (tomlData.model_providers) {
2799
2820
  for (const [id, providerData] of Object.entries(tomlData.model_providers)) {
@@ -2862,21 +2883,30 @@ function parseCodexConfig(content) {
2862
2883
  const otherConfig = [];
2863
2884
  const lines = content.split("\n");
2864
2885
  let skipCurrentSection = false;
2865
- let currentSection = "";
2866
2886
  for (const line of lines) {
2867
- const trimmedLine = line.trim();
2868
- if (trimmedLine.includes("--- model provider added by ZCF ---") || trimmedLine.includes("--- MCP servers added by ZCF ---") || trimmedLine.includes("Managed by ZCF")) {
2887
+ const trimmed = line.trim();
2888
+ if (!trimmed)
2869
2889
  continue;
2870
- }
2871
- const sectionMatch = trimmedLine.match(/^\[([^\]]+)\]/);
2872
- if (sectionMatch) {
2873
- currentSection = sectionMatch[1];
2874
- skipCurrentSection = currentSection.startsWith("model_providers.") || currentSection.startsWith("mcp_servers.");
2875
- }
2876
- if (!skipCurrentSection && (trimmedLine.startsWith("model_provider") || trimmedLine.startsWith("# model_provider") || /^model\s*=/.test(trimmedLine))) {
2890
+ if (/^#\s*---\s*model provider added by ZCF\s*---\s*$/i.test(trimmed))
2891
+ continue;
2892
+ if (/^#\s*---\s*MCP servers added by ZCF\s*---\s*$/i.test(trimmed))
2893
+ continue;
2894
+ if (/Managed by ZCF/i.test(trimmed))
2895
+ continue;
2896
+ const sec = trimmed.match(/^\[([^\]]+)\]/);
2897
+ if (sec) {
2898
+ const name = sec[1];
2899
+ skipCurrentSection = name.startsWith("model_providers.") || name.startsWith("mcp_servers.");
2900
+ if (skipCurrentSection)
2901
+ continue;
2902
+ otherConfig.push(line);
2877
2903
  continue;
2878
2904
  }
2879
- if (!skipCurrentSection && trimmedLine) {
2905
+ if (/^#?\s*model_provider\s*=/.test(trimmed))
2906
+ continue;
2907
+ if (/^model\s*=/.test(trimmed))
2908
+ continue;
2909
+ if (!skipCurrentSection) {
2880
2910
  otherConfig.push(line);
2881
2911
  }
2882
2912
  }
@@ -2892,13 +2922,15 @@ function parseCodexConfig(content) {
2892
2922
  };
2893
2923
  } catch (error) {
2894
2924
  console.warn("TOML parsing failed, falling back to basic parsing:", error);
2925
+ const cleaned = content.replace(/^\s*#\s*---\s*model provider added by ZCF\s*---\s*$/gim, "").replace(/^\s*#\s*---\s*MCP servers added by ZCF\s*---\s*$/gim, "").replace(/^\[model_providers\.[^\]]+\][\s\S]*?(?=^\[|$)/gim, "").replace(/^\[mcp_servers\.[^\]]+\][\s\S]*?(?=^\[|$)/gim, "").replace(/^\s*(?:#\s*)?model_provider\s*=.*$/gim, "").replace(/^\s*model\s*=.*$/gim, "").replace(/\n{3,}/g, "\n\n");
2926
+ const otherConfig = cleaned.split("\n").map((l) => l.replace(/\s+$/g, "")).filter((l) => l.trim().length > 0);
2895
2927
  return {
2896
2928
  model: null,
2897
2929
  modelProvider: null,
2898
2930
  providers: [],
2899
2931
  mcpServices: [],
2900
2932
  managed: false,
2901
- otherConfig: content.split("\n"),
2933
+ otherConfig,
2902
2934
  modelProviderCommented: void 0
2903
2935
  };
2904
2936
  }
@@ -2927,9 +2959,29 @@ function renderCodexConfig(data) {
2927
2959
  lines.push("");
2928
2960
  }
2929
2961
  if (data.otherConfig && data.otherConfig.length > 0) {
2930
- lines.push(...data.otherConfig);
2931
- if (data.providers.length > 0 || data.mcpServices.length > 0) {
2932
- lines.push("");
2962
+ const preserved = data.otherConfig.filter((raw) => {
2963
+ const l = String(raw).trim();
2964
+ if (!l)
2965
+ return false;
2966
+ if (/^#\s*---\s*model provider added by ZCF\s*---\s*$/i.test(l))
2967
+ return false;
2968
+ if (/^#\s*---\s*MCP servers added by ZCF\s*---\s*$/i.test(l))
2969
+ return false;
2970
+ if (/^\[\s*mcp_servers\./i.test(l))
2971
+ return false;
2972
+ if (/^\[\s*model_providers\./i.test(l))
2973
+ return false;
2974
+ if (/^#?\s*model_provider\s*=/.test(l))
2975
+ return false;
2976
+ if (/^\s*model\s*=/.test(l))
2977
+ return false;
2978
+ return true;
2979
+ });
2980
+ if (preserved.length > 0) {
2981
+ lines.push(...preserved);
2982
+ if (data.providers.length > 0 || data.mcpServices.length > 0) {
2983
+ lines.push("");
2984
+ }
2933
2985
  }
2934
2986
  }
2935
2987
  if (data.providers.length > 0) {
@@ -3438,14 +3490,29 @@ async function configureCodexMcp() {
3438
3490
  const servicesMeta = await getMcpServices();
3439
3491
  const baseProviders = existingConfig?.providers || [];
3440
3492
  const selection = [];
3441
- const existingMap = new Map((existingConfig?.mcpServices || []).map((service) => [service.id, service]));
3493
+ const existingServices = existingConfig?.mcpServices || [];
3442
3494
  if (selectedIds.length === 0) {
3443
3495
  console.log(ansis.yellow(i18n.t("codex:noMcpConfigured")));
3496
+ const preserved = (existingServices || []).map((svc) => {
3497
+ if (isWindows()) {
3498
+ const systemRoot = getSystemRoot();
3499
+ if (systemRoot) {
3500
+ return {
3501
+ ...svc,
3502
+ env: {
3503
+ ...svc.env || {},
3504
+ SYSTEMROOT: systemRoot
3505
+ }
3506
+ };
3507
+ }
3508
+ }
3509
+ return svc;
3510
+ });
3444
3511
  writeCodexConfig({
3445
3512
  model: existingConfig?.model || null,
3446
3513
  modelProvider: existingConfig?.modelProvider || null,
3447
3514
  providers: baseProviders,
3448
- mcpServices: Array.from(existingMap.values()),
3515
+ mcpServices: preserved,
3449
3516
  otherConfig: existingConfig?.otherConfig || []
3450
3517
  });
3451
3518
  updateZcfConfig({ codeToolType: "codex" });
@@ -3457,10 +3524,18 @@ async function configureCodexMcp() {
3457
3524
  continue;
3458
3525
  const serviceMeta = servicesMeta.find((service) => service.id === id);
3459
3526
  let command = configInfo.config.command || id;
3460
- const args = (configInfo.config.args || []).map((arg) => String(arg));
3461
- if (isWindows() && command === "npx")
3462
- command = "npx.cmd";
3527
+ let args = (configInfo.config.args || []).map((arg) => String(arg));
3528
+ const serviceConfig = { id: id.toLowerCase(), command, args };
3529
+ applyCodexPlatformCommand(serviceConfig);
3530
+ command = serviceConfig.command;
3531
+ args = serviceConfig.args || [];
3463
3532
  const env = { ...configInfo.config.env || {} };
3533
+ if (isWindows()) {
3534
+ const systemRoot = getSystemRoot();
3535
+ if (systemRoot) {
3536
+ env.SYSTEMROOT = systemRoot;
3537
+ }
3538
+ }
3464
3539
  if (configInfo.requiresApiKey && configInfo.apiKeyEnvVar) {
3465
3540
  const promptMessage = serviceMeta?.apiKeyPrompt || i18n.t("mcp:apiKeyPrompt");
3466
3541
  const { apiKey } = await inquirer.prompt([{
@@ -3476,21 +3551,39 @@ async function configureCodexMcp() {
3476
3551
  selection.push({
3477
3552
  id: id.toLowerCase(),
3478
3553
  // Convert to lowercase for Codex compatibility
3479
- command,
3480
- args,
3554
+ command: serviceConfig.command,
3555
+ args: serviceConfig.args,
3481
3556
  env: Object.keys(env).length > 0 ? env : void 0,
3482
3557
  startup_timeout_ms: configInfo.config.startup_timeout_ms
3483
3558
  });
3484
3559
  }
3485
- const selectionMap = new Map(selection.map((service) => [service.id, service]));
3486
- const mergedMap = new Map(existingMap);
3487
- for (const service of selectionMap.values())
3488
- mergedMap.set(service.id, service);
3560
+ const mergedMap = /* @__PURE__ */ new Map();
3561
+ for (const svc of existingServices) {
3562
+ mergedMap.set(svc.id.toLowerCase(), { ...svc });
3563
+ }
3564
+ for (const svc of selection) {
3565
+ mergedMap.set(svc.id.toLowerCase(), { ...svc });
3566
+ }
3567
+ const finalServices = Array.from(mergedMap.values()).map((svc) => {
3568
+ if (isWindows()) {
3569
+ const systemRoot = getSystemRoot();
3570
+ if (systemRoot) {
3571
+ return {
3572
+ ...svc,
3573
+ env: {
3574
+ ...svc.env || {},
3575
+ SYSTEMROOT: systemRoot
3576
+ }
3577
+ };
3578
+ }
3579
+ }
3580
+ return svc;
3581
+ });
3489
3582
  writeCodexConfig({
3490
3583
  model: existingConfig?.model || null,
3491
3584
  modelProvider: existingConfig?.modelProvider || null,
3492
3585
  providers: baseProviders,
3493
- mcpServices: Array.from(mergedMap.values()),
3586
+ mcpServices: finalServices,
3494
3587
  otherConfig: existingConfig?.otherConfig || []
3495
3588
  });
3496
3589
  updateZcfConfig({ codeToolType: "codex" });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zcf",
3
3
  "type": "module",
4
- "version": "3.1.1",
4
+ "version": "3.1.3",
5
5
  "description": "Zero-Config Code Flow - One-click configuration tool for Claude Code",
6
6
  "author": {
7
7
  "name": "Miao Da",
@@ -10,17 +10,19 @@
10
10
  "permissions": {
11
11
  "allow": [
12
12
  "Bash",
13
- "Read",
14
- "Write",
13
+ "BashOutput",
15
14
  "Edit",
16
- "MultiEdit",
17
15
  "Glob",
18
16
  "Grep",
17
+ "KillShell",
18
+ "NotebookEdit",
19
+ "Read",
20
+ "SlashCommand",
21
+ "Task",
22
+ "TodoWrite",
19
23
  "WebFetch",
20
24
  "WebSearch",
21
- "TodoWrite",
22
- "NotebookRead",
23
- "NotebookEdit",
25
+ "Write",
24
26
  "mcp__ide",
25
27
  "mcp__exa",
26
28
  "mcp__context7",