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 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.7",
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
- const content = fs2.readFileSync(configPath, "utf-8");
319
- userConfig = parseToml(content);
288
+ try {
289
+ backupFile(configPath);
290
+ } catch {
291
+ }
320
292
  }
321
293
  const templateConfig = loadCodexTemplateConfig();
322
- const mergedConfig = deepMerge(templateConfig, userConfig);
323
- if (mergedConfig.features && typeof mergedConfig.features === "object" && !Array.isArray(mergedConfig.features) && "web_search_request" in mergedConfig.features) {
324
- delete mergedConfig.features.web_search_request;
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 (mergedConfig.features && typeof mergedConfig.features === "object" && !Array.isArray(mergedConfig.features)) {
330
- for (const key of ["plan_tool", "view_image_tool", "rmcp_client", "streamable_shell"]) {
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
- mergedConfig.model_provider = providerKey;
341
- mergedConfig.model = provider.model || mergedConfig.model || "gpt-5.2-codex";
342
- mergedConfig.model_providers = mergedConfig.model_providers || {};
343
- if (providerKey !== provider.name) {
344
- delete mergedConfig.model_providers[provider.name];
345
- }
346
- mergedConfig.model_providers[providerKey] = {
347
- name: providerKey,
348
- base_url: provider.baseUrl,
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(mergedConfig), { mode: 384 });
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 = "high"
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccman",
3
- "version": "3.3.7",
3
+ "version": "3.3.8",
4
4
  "type": "module",
5
5
  "description": "Manage Codex, Claude Code, Gemini CLI, OpenCode, and MCP API service provider configurations",
6
6
  "main": "./dist/index.js",