@kkmila/cpc 1.0.1 → 1.0.5

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.
@@ -0,0 +1,115 @@
1
+ import { homedir } from "node:os";
2
+ import {
3
+ existsSync,
4
+ readFileSync,
5
+ writeFileSync,
6
+ appendFileSync,
7
+ } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { execSync } from "node:child_process";
10
+
11
+ /**
12
+ * 跨平台环境变量持久化模块。
13
+ *
14
+ * codex 的 model_providers 只能用 `env_key` 引用环境变量,
15
+ * 不能内联 key。cpc 安装到 codex 时,需要把 provider.apiKey
16
+ * 落地到一个环境变量里,并保证新开的终端/codex 进程能读到。
17
+ *
18
+ * 平台策略:
19
+ * - Linux / macOS: 按 $SHELL 检测写 ~/.zshrc 或 ~/.bashrc(幂等)
20
+ * - Windows: 用 setx 写入用户环境变量(注册表 HKCU\Environment)
21
+ */
22
+
23
+ /**
24
+ * 推断当前 shell 的 rc 文件路径。
25
+ * @returns {string|null} rc 文件绝对路径;Windows 返回 null
26
+ */
27
+ function detectShellRc() {
28
+ const platform = process.platform;
29
+ if (platform === "win32") return null;
30
+
31
+ const shell = process.env.SHELL || "";
32
+ const home = homedir();
33
+ if (shell.includes("zsh")) return join(home, ".zshrc");
34
+ if (shell.includes("bash")) return join(home, ".bashrc");
35
+
36
+ // $SHELL 缺失时按平台兜底:macOS 默认 zsh,其余默认 bash
37
+ if (platform === "darwin") return join(home, ".zshrc");
38
+ return join(home, ".bashrc");
39
+ }
40
+
41
+ /**
42
+ * 幂等地把 `export KEY="value"` 写入 rc 文件。
43
+ * 已存在该 export 行则替换值,不存在则追加。
44
+ */
45
+ function persistToRc(rcPath, key, value) {
46
+ const exportLine = `export ${key}="${value}"`;
47
+ let content = "";
48
+ if (existsSync(rcPath)) {
49
+ content = readFileSync(rcPath, "utf8");
50
+ }
51
+ // 精确匹配 `export KEY=...` 整行
52
+ const regex = new RegExp(`^export ${key}=.*$`, "m");
53
+ if (regex.test(content)) {
54
+ const updated = content.replace(regex, exportLine);
55
+ if (updated !== content) {
56
+ writeFileSync(rcPath, updated);
57
+ }
58
+ return false; // 不是新增
59
+ }
60
+ const sep = content && !content.endsWith("\n") ? "\n" : "";
61
+ const block = `${sep}# Added by cpc (codex provider)\n${exportLine}\n`;
62
+ appendFileSync(rcPath, block);
63
+ return true; // 新增
64
+ }
65
+
66
+ /**
67
+ * Windows: 用 setx 持久化用户环境变量。
68
+ * 注意 setx 设置的变量只在新进程生效,当前进程不会自动有。
69
+ */
70
+ function persistWithSetx(key, value) {
71
+ // setx 对带空格的值需要引号;API key 一般是 token 串,安全起见仍加引号
72
+ execSync(`setx ${key} "${value}"`, {
73
+ shell: "cmd.exe",
74
+ stdio: "ignore",
75
+ });
76
+ }
77
+
78
+ /**
79
+ * 跨平台持久化环境变量。
80
+ *
81
+ * @param {string} key 环境变量名
82
+ * @param {string} value 值
83
+ * @returns {{method: "rc"|"setx"|"skip", target: string, created: boolean}}
84
+ */
85
+ export function persistEnvVar(key, value) {
86
+ if (!key || value == null) {
87
+ return { method: "skip", target: "", created: false };
88
+ }
89
+
90
+ const rc = detectShellRc();
91
+ if (rc) {
92
+ const created = persistToRc(rc, key, value);
93
+ return { method: "rc", target: rc, created };
94
+ }
95
+
96
+ // Windows
97
+ try {
98
+ persistWithSetx(key, value);
99
+ return { method: "setx", target: "HKCU\\Environment", created: true };
100
+ } catch (err) {
101
+ throw new Error(`Failed to set env var on Windows: ${err.message}`);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * 把 provider 名转成规范环境变量名:CPC_<NAME_UPPER>_API_KEY
107
+ * 非字母数字字符 -> _
108
+ */
109
+ export function envKeyNameForProvider(providerName) {
110
+ const safe = String(providerName)
111
+ .toUpperCase()
112
+ .replace(/[^A-Z0-9]+/g, "_")
113
+ .replace(/^_+|_+$/g, "");
114
+ return `CPC_${safe}_API_KEY`;
115
+ }