zcf 3.3.2 → 3.3.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.
package/README.md CHANGED
@@ -29,6 +29,13 @@
29
29
 
30
30
  ---
31
31
 
32
+ [![GLM](./src/assets/GLM-en.png)](https://z.ai/subscribe?ic=8JVLJQFSKB)
33
+ This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN.
34
+ GLM CODING PLAN is a subscription service designed for AI coding, starting at just $3/month. It provides access to their flagship GLM-4.6 model across 10+ popular AI coding tools (Claude Code, Cline, Roo Code, etc.), offering developers top-tier, fast, and stable coding experiences.
35
+ Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB
36
+
37
+ ---
38
+
32
39
  <table>
33
40
  <tr>
34
41
  <td width="180"><a href="https://www.packyapi.com/register?aff=zcf"><img src="./src/assets/packycode.png" alt="PackyCode" width="150"></a></td>
@@ -164,7 +171,7 @@ ZCF now supports API provider presets that automatically configure baseUrl and m
164
171
  **Supported Providers:**
165
172
  - `302ai` - [302.AI](https://share.302.ai/gAT9VG) API Service
166
173
  - `packycode` - [PackyCode](https://www.packyapi.com/register?aff=zcf) API Service
167
- - `glm` - GLM (z.ai)
174
+ - `glm` - [GLM](https://z.ai/subscribe?ic=8JVLJQFSKB) API Service
168
175
  - `minimax` - MiniMax API Service
169
176
  - `kimi` - Kimi (Moonshot AI)
170
177
  - `custom` - Custom API endpoint (requires manual URL configuration)
@@ -944,6 +951,7 @@ If you find this project helpful, please consider sponsoring its development. Yo
944
951
 
945
952
  A huge thank you to all our sponsors for their generous support!
946
953
  - [302.AI](https://share.302.ai/gAT9VG) (first corporate sponsorship 🤠)
954
+ - [GLM](https://z.ai/subscribe?ic=8JVLJQFSKB) (first AI model sponsorship 🤖)
947
955
  - [PackyCode](https://www.packyapi.com/register?aff=zcf) (first API proxy service sponsor 🧝🏻‍♀️)
948
956
  - Tc (first sponsor)
949
957
  - Argolinhas (first ko-fi sponsor ٩(•̤̀ᵕ•̤́๑))
@@ -1,4 +1,5 @@
1
- import { existsSync, readFileSync, mkdirSync, copyFileSync, writeFileSync, rmSync, rmdirSync, readdirSync, statSync, unlinkSync, renameSync } from 'node:fs';
1
+ import * as nodeFs from 'node:fs';
2
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, rmSync, rmdirSync, readdirSync, statSync, unlinkSync, renameSync } from 'node:fs';
2
3
  import process from 'node:process';
3
4
  import ansis from 'ansis';
4
5
  import inquirer from 'inquirer';
@@ -16,7 +17,7 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
16
17
  import i18next from 'i18next';
17
18
  import Backend from 'i18next-fs-backend';
18
19
 
19
- const version = "3.3.2";
20
+ const version = "3.3.3";
20
21
  const homepage = "https://github.com/UfoMiao/zcf";
21
22
 
22
23
  const i18n = i18next.createInstance();
@@ -523,7 +524,7 @@ function getPlatform() {
523
524
  return "linux";
524
525
  }
525
526
  function isTermux() {
526
- return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
527
+ return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || nodeFs.existsSync("/data/data/com.termux/files/usr");
527
528
  }
528
529
  function getTermuxPrefix() {
529
530
  return process.env.PREFIX || "/data/data/com.termux/files/usr";
@@ -535,16 +536,16 @@ function isWSL() {
535
536
  if (process.env.WSL_DISTRO_NAME) {
536
537
  return true;
537
538
  }
538
- if (existsSync("/proc/version")) {
539
+ if (nodeFs.existsSync("/proc/version")) {
539
540
  try {
540
- const version = readFileSync("/proc/version", "utf8");
541
+ const version = nodeFs.readFileSync("/proc/version", "utf8");
541
542
  if (version.includes("Microsoft") || version.includes("WSL")) {
542
543
  return true;
543
544
  }
544
545
  } catch {
545
546
  }
546
547
  }
547
- if (existsSync("/mnt/c")) {
548
+ if (nodeFs.existsSync("/mnt/c")) {
548
549
  return true;
549
550
  }
550
551
  return false;
@@ -553,9 +554,9 @@ function getWSLDistro() {
553
554
  if (process.env.WSL_DISTRO_NAME) {
554
555
  return process.env.WSL_DISTRO_NAME;
555
556
  }
556
- if (existsSync("/etc/os-release")) {
557
+ if (nodeFs.existsSync("/etc/os-release")) {
557
558
  try {
558
- const osRelease = readFileSync("/etc/os-release", "utf8");
559
+ const osRelease = nodeFs.readFileSync("/etc/os-release", "utf8");
559
560
  const nameMatch = osRelease.match(/^PRETTY_NAME="(.+)"$/m);
560
561
  if (nameMatch) {
561
562
  return nameMatch[1];
@@ -570,9 +571,9 @@ function getWSLInfo() {
570
571
  return null;
571
572
  }
572
573
  let version = null;
573
- if (existsSync("/proc/version")) {
574
+ if (nodeFs.existsSync("/proc/version")) {
574
575
  try {
575
- version = readFileSync("/proc/version", "utf8").trim();
576
+ version = nodeFs.readFileSync("/proc/version", "utf8").trim();
576
577
  } catch {
577
578
  }
578
579
  }
@@ -599,6 +600,71 @@ function getSystemRoot() {
599
600
  systemRoot = env.SystemRoot;
600
601
  return systemRoot.replace(/\\+/g, "/").replace(/\/+/g, "/");
601
602
  }
603
+ function shouldUseSudoForGlobalInstall() {
604
+ if (isTermux())
605
+ return false;
606
+ if (getPlatform() !== "linux")
607
+ return false;
608
+ const npmPrefix = getGlobalNpmPrefix();
609
+ if (npmPrefix) {
610
+ if (isPathInsideHome(npmPrefix) || canWriteToPath(npmPrefix))
611
+ return false;
612
+ }
613
+ const getuid = process.getuid;
614
+ if (typeof getuid !== "function")
615
+ return false;
616
+ try {
617
+ return getuid() !== 0;
618
+ } catch {
619
+ return false;
620
+ }
621
+ }
622
+ function wrapCommandWithSudo(command, args) {
623
+ if (shouldUseSudoForGlobalInstall()) {
624
+ return {
625
+ command: "sudo",
626
+ args: [command, ...args],
627
+ usedSudo: true
628
+ };
629
+ }
630
+ return {
631
+ command,
632
+ args,
633
+ usedSudo: false
634
+ };
635
+ }
636
+ const WRITE_CHECK_FLAG = 2;
637
+ function normalizePath(path) {
638
+ return path.replace(/\\/g, "/").replace(/\/+$/, "");
639
+ }
640
+ function isPathInsideHome(path) {
641
+ const home = process.env.HOME;
642
+ if (!home)
643
+ return false;
644
+ const normalizedHome = normalizePath(home);
645
+ const normalizedPath = normalizePath(path);
646
+ return normalizedPath === normalizedHome || normalizedPath.startsWith(`${normalizedHome}/`);
647
+ }
648
+ function canWriteToPath(path) {
649
+ try {
650
+ nodeFs.accessSync(path, WRITE_CHECK_FLAG);
651
+ return true;
652
+ } catch {
653
+ return false;
654
+ }
655
+ }
656
+ function getGlobalNpmPrefix() {
657
+ const env = process.env;
658
+ const envPrefix = env.npm_config_prefix || env.NPM_CONFIG_PREFIX || env.PREFIX;
659
+ if (envPrefix)
660
+ return envPrefix;
661
+ const execPath = process.execPath;
662
+ if (execPath) {
663
+ const binDir = dirname(execPath);
664
+ return dirname(binDir);
665
+ }
666
+ return null;
667
+ }
602
668
  async function commandExists(command) {
603
669
  try {
604
670
  const cmd = getPlatform() === "windows" ? "where" : "which";
@@ -616,7 +682,7 @@ async function commandExists(command) {
616
682
  `/data/data/com.termux/files/usr/bin/${command}`
617
683
  ];
618
684
  for (const path of possiblePaths) {
619
- if (existsSync(path)) {
685
+ if (nodeFs.existsSync(path)) {
620
686
  return true;
621
687
  }
622
688
  }
@@ -629,7 +695,7 @@ async function commandExists(command) {
629
695
  `${process.env.HOME}/.local/bin/${command}`
630
696
  ];
631
697
  for (const path of commonPaths) {
632
- if (existsSync(path)) {
698
+ if (nodeFs.existsSync(path)) {
633
699
  return true;
634
700
  }
635
701
  }
@@ -2219,7 +2285,12 @@ async function installCcr() {
2219
2285
  }
2220
2286
  console.log(ansis.cyan(`\u{1F4E6} ${i18n.t("ccr:installingCcr")}`));
2221
2287
  try {
2222
- await execAsync$1("npm install -g @musistudio/claude-code-router --force");
2288
+ const installArgs = ["install", "-g", "@musistudio/claude-code-router", "--force"];
2289
+ const { command, args, usedSudo } = wrapCommandWithSudo("npm", installArgs);
2290
+ if (usedSudo) {
2291
+ console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
2292
+ }
2293
+ await execAsync$1([command, ...args].join(" "));
2223
2294
  console.log(ansis.green(`\u2714 ${i18n.t("ccr:ccrInstallSuccess")}`));
2224
2295
  } catch (error) {
2225
2296
  if (error.message?.includes("EEXIST")) {
@@ -2938,7 +3009,10 @@ async function executeCodexInstallation(isUpdate) {
2938
3009
  } else {
2939
3010
  console.log(ansis.cyan(i18n.t("codex:installingCli")));
2940
3011
  }
2941
- const result = await x("npm", ["install", "-g", "@openai/codex"]);
3012
+ const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@openai/codex"]);
3013
+ if (usedSudo)
3014
+ console.log(ansis.yellow(i18n.t("codex:usingSudo")));
3015
+ const result = await x(command, args);
2942
3016
  if (result.exitCode !== 0) {
2943
3017
  throw new Error(`Failed to ${action} codex CLI: exit code ${result.exitCode}`);
2944
3018
  }
@@ -3970,7 +4044,10 @@ async function runCodexUpdate(force = false, skipPrompt = false) {
3970
4044
  async function runCodexUninstall() {
3971
4045
  ensureI18nInitialized();
3972
4046
  const { CodexUninstaller } = await import('./codex-uninstaller.mjs');
3973
- const uninstaller = new CodexUninstaller("en");
4047
+ const zcfConfig = readZcfConfig();
4048
+ const preferredLang = zcfConfig?.preferredLang;
4049
+ const uninstallLang = preferredLang && SUPPORTED_LANGS.includes(preferredLang) ? preferredLang : "en";
4050
+ const uninstaller = new CodexUninstaller(uninstallLang);
3974
4051
  const { mode } = await inquirer.prompt([{
3975
4052
  type: "list",
3976
4053
  name: "mode",
@@ -4284,12 +4361,20 @@ async function isCometixLineInstalled() {
4284
4361
  }
4285
4362
  async function installCometixLine() {
4286
4363
  ensureI18nInitialized();
4364
+ const runInstallCommand = async () => {
4365
+ const installArgs = ["install", "-g", COMETIX_PACKAGE_NAME];
4366
+ const { command, args, usedSudo } = wrapCommandWithSudo("npm", installArgs);
4367
+ if (usedSudo) {
4368
+ console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
4369
+ }
4370
+ await execAsync([command, ...args].join(" "));
4371
+ };
4287
4372
  const isInstalled = await isCometixLineInstalled();
4288
4373
  if (isInstalled) {
4289
4374
  console.log(ansis.green(`\u2714 ${i18n.t("cometix:cometixAlreadyInstalled")}`));
4290
4375
  try {
4291
4376
  console.log(ansis.blue(`${i18n.t("cometix:installingOrUpdating")}`));
4292
- await execAsync(COMETIX_COMMANDS.INSTALL);
4377
+ await runInstallCommand();
4293
4378
  console.log(ansis.green(`\u2714 ${i18n.t("cometix:installUpdateSuccess")}`));
4294
4379
  } catch (error) {
4295
4380
  console.log(ansis.yellow(`\u26A0 ${i18n.t("cometix:installUpdateFailed")}: ${error}`));
@@ -4308,7 +4393,7 @@ async function installCometixLine() {
4308
4393
  }
4309
4394
  try {
4310
4395
  console.log(ansis.blue(`${i18n.t("cometix:installingCometix")}`));
4311
- await execAsync(COMETIX_COMMANDS.INSTALL);
4396
+ await runInstallCommand();
4312
4397
  console.log(ansis.green(`\u2714 ${i18n.t("cometix:cometixInstallSuccess")}`));
4313
4398
  try {
4314
4399
  addCCometixLineConfig();
@@ -4789,7 +4874,11 @@ async function installClaudeCode() {
4789
4874
  }
4790
4875
  console.log(i18n.t("installation:installing"));
4791
4876
  try {
4792
- await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
4877
+ const { command, args, usedSudo } = wrapCommandWithSudo("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
4878
+ if (usedSudo) {
4879
+ console.log(ansis.yellow(`\u2139 ${i18n.t("installation:usingSudo")}`));
4880
+ }
4881
+ await exec(command, args);
4793
4882
  console.log(`\u2714 ${i18n.t("installation:installSuccess")}`);
4794
4883
  await setInstallMethod("npm");
4795
4884
  if (isTermux()) {
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "installingCli": "🚀 Installing Codex CLI...",
3
3
  "installSuccess": "✔ Codex CLI installed",
4
+ "usingSudo": "⚠️ Detected non-root Linux user. Using sudo for Codex CLI installation (password prompt may appear).",
4
5
  "alreadyInstalled": "⚠️ Codex CLI already installed, skipping installation",
5
6
  "updatingCli": "🔄 Updating Codex CLI...",
6
7
  "updateSuccess": "✔ Codex CLI updated",
@@ -4,6 +4,7 @@
4
4
  "installPrompt": "Claude Code not found. Install automatically?",
5
5
  "installSuccess": "Claude Code installed successfully",
6
6
  "installing": "Installing Claude Code...",
7
+ "usingSudo": "Detected non-root Linux user. Elevating with sudo for global installation (password prompt may appear).",
7
8
  "termuxDetected": "Termux environment detected",
8
9
  "termuxEnvironmentInfo": "Termux environment provides Node.js and npm through pkg manager",
9
10
  "termuxInstallHint": "In Termux, please run first: pkg install nodejs or pkg install nodejs-lts",
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "installingCli": "🚀 正在安装 Codex CLI...",
3
3
  "installSuccess": "✔ Codex CLI 安装完成",
4
+ "usingSudo": "⚠️ 检测到 Linux 非 root 用户,正在使用 sudo 安装 Codex CLI(可能需要输入密码)。",
4
5
  "alreadyInstalled": "⚠️ Codex CLI 已安装,跳过安装步骤",
5
6
  "updatingCli": "🔄 正在更新 Codex CLI...",
6
7
  "updateSuccess": "✔ Codex CLI 更新完成",
@@ -4,6 +4,7 @@
4
4
  "installPrompt": "检测到 Claude Code 未安装,是否自动安装?",
5
5
  "installSuccess": "Claude Code 安装成功",
6
6
  "installing": "正在安装 Claude Code...",
7
+ "usingSudo": "检测到 Linux 非 root 用户,正在使用 sudo 提升权限进行全局安装(可能需要输入密码)。",
7
8
  "termuxDetected": "检测到 Termux 环境",
8
9
  "termuxEnvironmentInfo": "Termux 环境通过 pkg 管理器提供 Node.js 和 npm",
9
10
  "termuxInstallHint": "在 Termux 中,请先运行: pkg install nodejs 或 pkg install nodejs-lts",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zcf",
3
3
  "type": "module",
4
- "version": "3.3.2",
4
+ "version": "3.3.3",
5
5
  "description": "Zero-Config Code Flow - One-click configuration tool for Code Cli",
6
6
  "author": {
7
7
  "name": "Miao Da",