ccman 3.3.7 → 3.3.8
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 +4 -4
- package/dist/index.js +49 -61
- package/dist/templates/codex/config.toml +47 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- 🔄 **一键切换**:一条命令切换服务商,自动修改配置文件
|
|
10
10
|
- 📦 **内置预设**:提供常用预设(Claude: 1 个,Gemini: 2 个,Codex: 2 个,OpenCode: 1 个,MCP: 多个),只需填写 API Key
|
|
11
11
|
- 🛠️ **自定义配置**:支持添加任意第三方服务商
|
|
12
|
-
- 🔐
|
|
12
|
+
- 🔐 **安全写入**:写入前备份;Codex 的 `config.toml/auth.json` 备份后覆盖写入,其他工具尽量保留用户字段
|
|
13
13
|
- 🎯 **多工具支持**:同时管理 Codex、Claude Code、Gemini CLI、OpenCode 和 MCP 服务器
|
|
14
14
|
- 📱 **双界面**:提供 CLI(命令行)和 Desktop(图形界面)
|
|
15
15
|
- 🔁 **克隆功能**:快速复制配置,管理多个 API Key
|
|
@@ -350,9 +350,9 @@ $ ccman cc use "Claude Test"
|
|
|
350
350
|
- **Gemini CLI**: `~/.gemini/settings.json` 和 `~/.gemini/.env`
|
|
351
351
|
- **OpenCode**: `~/.config/opencode/opencode.json`
|
|
352
352
|
|
|
353
|
-
|
|
354
|
-
-
|
|
355
|
-
-
|
|
353
|
+
**安全写入承诺**:
|
|
354
|
+
- 写入前备份(`.bak`),失败时自动回滚
|
|
355
|
+
- Codex 的 `config.toml/auth.json` 采用备份后覆盖写入(避免遗留字段导致报错/告警)
|
|
356
356
|
- API Key 存储在本地,权限 `0600`
|
|
357
357
|
|
|
358
358
|
---
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"../core/package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@ccman/core",
|
|
18
|
-
version: "3.3.
|
|
18
|
+
version: "3.3.8",
|
|
19
19
|
type: "module",
|
|
20
20
|
description: "Core business logic for ccman - Manage Codex, Claude Code, Gemini CLI, and MCP configurations",
|
|
21
21
|
main: "./dist/index.js",
|
|
@@ -244,35 +244,6 @@ var init_file = __esm({
|
|
|
244
244
|
}
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
-
// ../core/dist/utils/template.js
|
|
248
|
-
function replaceVariables(template, variables) {
|
|
249
|
-
const jsonStr = JSON.stringify(template);
|
|
250
|
-
let result = jsonStr;
|
|
251
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
252
|
-
const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
253
|
-
result = result.replace(new RegExp(`{{${key}}}`, "g"), escapedValue);
|
|
254
|
-
}
|
|
255
|
-
return JSON.parse(result);
|
|
256
|
-
}
|
|
257
|
-
function deepMerge(target, source) {
|
|
258
|
-
const result = { ...target };
|
|
259
|
-
for (const key in source) {
|
|
260
|
-
const sourceValue = source[key];
|
|
261
|
-
const targetValue = result[key];
|
|
262
|
-
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
263
|
-
result[key] = deepMerge(targetValue, sourceValue);
|
|
264
|
-
} else {
|
|
265
|
-
result[key] = sourceValue;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return result;
|
|
269
|
-
}
|
|
270
|
-
var init_template = __esm({
|
|
271
|
-
"../core/dist/utils/template.js"() {
|
|
272
|
-
"use strict";
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
247
|
// ../core/dist/writers/codex.js
|
|
277
248
|
import * as fs2 from "fs";
|
|
278
249
|
import * as path3 from "path";
|
|
@@ -313,43 +284,32 @@ function loadCodexTemplateConfig() {
|
|
|
313
284
|
function writeCodexConfig(provider) {
|
|
314
285
|
ensureDir(getCodexDir());
|
|
315
286
|
const configPath = getCodexConfigPath();
|
|
316
|
-
let userConfig = {};
|
|
317
287
|
if (fileExists(configPath)) {
|
|
318
|
-
|
|
319
|
-
|
|
288
|
+
try {
|
|
289
|
+
backupFile(configPath);
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
320
292
|
}
|
|
321
293
|
const templateConfig = loadCodexTemplateConfig();
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
delete
|
|
325
|
-
}
|
|
326
|
-
if ("web_search_request" in mergedConfig) {
|
|
327
|
-
delete mergedConfig.web_search_request;
|
|
294
|
+
const nextConfig = { ...templateConfig };
|
|
295
|
+
if (nextConfig.features && typeof nextConfig.features === "object" && !Array.isArray(nextConfig.features) && "web_search_request" in nextConfig.features) {
|
|
296
|
+
delete nextConfig.features.web_search_request;
|
|
328
297
|
}
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
if (key in mergedConfig.features) {
|
|
332
|
-
delete mergedConfig.features[key];
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (Object.keys(mergedConfig.features).length === 0) {
|
|
336
|
-
delete mergedConfig.features;
|
|
337
|
-
}
|
|
298
|
+
if ("web_search_request" in nextConfig) {
|
|
299
|
+
delete nextConfig.web_search_request;
|
|
338
300
|
}
|
|
339
301
|
const providerKey = resolveCodexProviderKey(provider);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
wire_api: "responses",
|
|
350
|
-
requires_openai_auth: true
|
|
302
|
+
nextConfig.model_provider = providerKey;
|
|
303
|
+
nextConfig.model = provider.model || nextConfig.model || "gpt-5.2-codex";
|
|
304
|
+
nextConfig.model_providers = {
|
|
305
|
+
[providerKey]: {
|
|
306
|
+
name: providerKey,
|
|
307
|
+
base_url: provider.baseUrl,
|
|
308
|
+
wire_api: "responses",
|
|
309
|
+
requires_openai_auth: true
|
|
310
|
+
}
|
|
351
311
|
};
|
|
352
|
-
fs2.writeFileSync(configPath, stringifyToml(
|
|
312
|
+
fs2.writeFileSync(configPath, stringifyToml(nextConfig), { mode: 384 });
|
|
353
313
|
const authPath = getCodexAuthPath();
|
|
354
314
|
if (fileExists(authPath)) {
|
|
355
315
|
try {
|
|
@@ -366,7 +326,6 @@ var init_codex = __esm({
|
|
|
366
326
|
"use strict";
|
|
367
327
|
init_paths();
|
|
368
328
|
init_file();
|
|
369
|
-
init_template();
|
|
370
329
|
__filename = fileURLToPath(import.meta.url);
|
|
371
330
|
__dirname = path3.dirname(__filename);
|
|
372
331
|
CODEX_DEFAULT_CONFIG = {
|
|
@@ -380,6 +339,35 @@ var init_codex = __esm({
|
|
|
380
339
|
}
|
|
381
340
|
});
|
|
382
341
|
|
|
342
|
+
// ../core/dist/utils/template.js
|
|
343
|
+
function replaceVariables(template, variables) {
|
|
344
|
+
const jsonStr = JSON.stringify(template);
|
|
345
|
+
let result = jsonStr;
|
|
346
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
347
|
+
const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
348
|
+
result = result.replace(new RegExp(`{{${key}}}`, "g"), escapedValue);
|
|
349
|
+
}
|
|
350
|
+
return JSON.parse(result);
|
|
351
|
+
}
|
|
352
|
+
function deepMerge(target, source) {
|
|
353
|
+
const result = { ...target };
|
|
354
|
+
for (const key in source) {
|
|
355
|
+
const sourceValue = source[key];
|
|
356
|
+
const targetValue = result[key];
|
|
357
|
+
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
358
|
+
result[key] = deepMerge(targetValue, sourceValue);
|
|
359
|
+
} else {
|
|
360
|
+
result[key] = sourceValue;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
var init_template = __esm({
|
|
366
|
+
"../core/dist/utils/template.js"() {
|
|
367
|
+
"use strict";
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
383
371
|
// ../core/dist/writers/claude.js
|
|
384
372
|
import * as fs3 from "fs";
|
|
385
373
|
import * as path4 from "path";
|
|
@@ -1,13 +1,50 @@
|
|
|
1
|
-
########################################
|
|
2
|
-
# Codex 最小模板(ccman 默认)
|
|
3
|
-
#
|
|
4
|
-
# - model_provider 与 [model_providers.*] 由 ccman 根据当前服务商自动写入
|
|
5
|
-
# - 配置越少越稳定;需要扩展再手动加
|
|
6
|
-
########################################
|
|
7
|
-
|
|
8
1
|
model = "gpt-5.2-codex"
|
|
9
|
-
model_reasoning_effort = "
|
|
10
|
-
model_verbosity = "high"
|
|
11
|
-
network_access = "enabled"
|
|
2
|
+
model_reasoning_effort = "xhigh"
|
|
12
3
|
disable_response_storage = true
|
|
4
|
+
sandbox_mode = "danger-full-access"
|
|
13
5
|
windows_wsl_setup_acknowledged = true
|
|
6
|
+
approval_policy = "never"
|
|
7
|
+
profile = "auto-max"
|
|
8
|
+
file_opener = "vscode"
|
|
9
|
+
model_provider = "gmn"
|
|
10
|
+
web_search = "cached"
|
|
11
|
+
suppress_unstable_features_warning = true
|
|
12
|
+
|
|
13
|
+
[history]
|
|
14
|
+
persistence = "save-all"
|
|
15
|
+
|
|
16
|
+
[tui]
|
|
17
|
+
notifications = true
|
|
18
|
+
|
|
19
|
+
[shell_environment_policy]
|
|
20
|
+
inherit = "all"
|
|
21
|
+
ignore_default_excludes = false
|
|
22
|
+
|
|
23
|
+
[sandbox_workspace_write]
|
|
24
|
+
network_access = true
|
|
25
|
+
|
|
26
|
+
[features]
|
|
27
|
+
plan_tool = true
|
|
28
|
+
apply_patch_freeform = true
|
|
29
|
+
view_image_tool = true
|
|
30
|
+
unified_exec = false
|
|
31
|
+
streamable_shell = false
|
|
32
|
+
rmcp_client = true
|
|
33
|
+
elevated_windows_sandbox = true
|
|
34
|
+
|
|
35
|
+
[profiles.auto-max]
|
|
36
|
+
approval_policy = "never"
|
|
37
|
+
sandbox_mode = "workspace-write"
|
|
38
|
+
|
|
39
|
+
[profiles.review]
|
|
40
|
+
approval_policy = "on-request"
|
|
41
|
+
sandbox_mode = "workspace-write"
|
|
42
|
+
|
|
43
|
+
[notice]
|
|
44
|
+
hide_gpt5_1_migration_prompt = true
|
|
45
|
+
|
|
46
|
+
[model_providers.gmn]
|
|
47
|
+
name = "gmn"
|
|
48
|
+
base_url = "https://gmn.chuangzuoli.com"
|
|
49
|
+
wire_api = "responses"
|
|
50
|
+
requires_openai_auth = true
|