ccman 3.3.10 → 3.3.12

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/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.10",
18
+ version: "3.3.12",
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",
@@ -300,7 +300,7 @@ function writeCodexConfig(provider) {
300
300
  }
301
301
  const providerKey = resolveCodexProviderKey(provider);
302
302
  nextConfig.model_provider = providerKey;
303
- nextConfig.model = provider.model || nextConfig.model || "gpt-5.2-codex";
303
+ nextConfig.model = provider.model || nextConfig.model || "gpt-5.3-codex";
304
304
  nextConfig.model_providers = {
305
305
  [providerKey]: {
306
306
  name: providerKey,
@@ -329,12 +329,51 @@ var init_codex = __esm({
329
329
  __filename = fileURLToPath(import.meta.url);
330
330
  __dirname = path3.dirname(__filename);
331
331
  CODEX_DEFAULT_CONFIG = {
332
- model: "gpt-5.2-codex",
333
- model_reasoning_effort: "high",
334
- model_verbosity: "high",
335
- network_access: "enabled",
332
+ model: "gpt-5.3-codex",
333
+ model_reasoning_effort: "xhigh",
336
334
  disable_response_storage: true,
337
- windows_wsl_setup_acknowledged: true
335
+ sandbox_mode: "danger-full-access",
336
+ windows_wsl_setup_acknowledged: true,
337
+ approval_policy: "never",
338
+ profile: "auto-max",
339
+ file_opener: "vscode",
340
+ web_search: "cached",
341
+ suppress_unstable_features_warning: true,
342
+ history: {
343
+ persistence: "save-all"
344
+ },
345
+ tui: {
346
+ notifications: true
347
+ },
348
+ shell_environment_policy: {
349
+ inherit: "all",
350
+ ignore_default_excludes: false
351
+ },
352
+ sandbox_workspace_write: {
353
+ network_access: true
354
+ },
355
+ features: {
356
+ plan_tool: true,
357
+ apply_patch_freeform: true,
358
+ view_image_tool: true,
359
+ unified_exec: false,
360
+ streamable_shell: false,
361
+ rmcp_client: true,
362
+ elevated_windows_sandbox: true
363
+ },
364
+ profiles: {
365
+ "auto-max": {
366
+ approval_policy: "never",
367
+ sandbox_mode: "workspace-write"
368
+ },
369
+ review: {
370
+ approval_policy: "on-request",
371
+ sandbox_mode: "workspace-write"
372
+ }
373
+ },
374
+ notice: {
375
+ hide_gpt5_1_migration_prompt: true
376
+ }
338
377
  };
339
378
  }
340
379
  });
@@ -748,10 +787,47 @@ var init_opencode = __esm({
748
787
 
749
788
  // ../core/dist/writers/gemini.js
750
789
  import * as fs5 from "fs";
751
- function loadEnvFile(envPath) {
752
- if (!fileExists(envPath))
753
- return {};
754
- const content = fs5.readFileSync(envPath, "utf-8");
790
+ import * as path6 from "path";
791
+ import { fileURLToPath as fileURLToPath3 } from "url";
792
+ function resolveTemplatePath3(relativePath) {
793
+ const candidates = [
794
+ // @ccman/core runtime (dist/writers -> templates)
795
+ path6.resolve(__dirname3, "../../templates", relativePath),
796
+ // Bundled CLI runtime (dist -> dist/templates)
797
+ path6.resolve(__dirname3, "templates", relativePath),
798
+ // Fallback (some bundlers/layouts)
799
+ path6.resolve(__dirname3, "../templates", relativePath)
800
+ ];
801
+ for (const candidate of candidates) {
802
+ if (fs5.existsSync(candidate))
803
+ return candidate;
804
+ }
805
+ return null;
806
+ }
807
+ function loadGeminiSettingsTemplate() {
808
+ try {
809
+ const templatePath = resolveTemplatePath3("gemini/settings.json");
810
+ if (templatePath) {
811
+ const content = fs5.readFileSync(templatePath, "utf-8");
812
+ return JSON.parse(content);
813
+ }
814
+ } catch {
815
+ }
816
+ return GEMINI_SETTINGS_TEMPLATE;
817
+ }
818
+ function loadGeminiEnvTemplate(provider) {
819
+ let templateContent = GEMINI_ENV_TEMPLATE;
820
+ try {
821
+ const templatePath = resolveTemplatePath3("gemini/.env");
822
+ if (templatePath) {
823
+ templateContent = fs5.readFileSync(templatePath, "utf-8");
824
+ }
825
+ } catch {
826
+ }
827
+ const content = templateContent.replaceAll("{{baseUrl}}", provider.baseUrl || "").replaceAll("{{apiKey}}", provider.apiKey || "");
828
+ return parseEnvContent(content);
829
+ }
830
+ function parseEnvContent(content) {
755
831
  const result = {};
756
832
  for (const line of content.split("\n")) {
757
833
  const trimmed = line.trim();
@@ -768,6 +844,12 @@ function loadEnvFile(envPath) {
768
844
  }
769
845
  return result;
770
846
  }
847
+ function loadEnvFile(envPath) {
848
+ if (!fileExists(envPath))
849
+ return {};
850
+ const content = fs5.readFileSync(envPath, "utf-8");
851
+ return parseEnvContent(content);
852
+ }
771
853
  function saveEnvFile(envPath, env) {
772
854
  const lines = [];
773
855
  const keys = Object.keys(env).sort();
@@ -782,18 +864,20 @@ function writeGeminiConfig(provider) {
782
864
  const envPath = getGeminiEnvPath();
783
865
  const dir = getGeminiDir();
784
866
  ensureDir(dir);
785
- let settings = {};
867
+ let userSettings = {};
786
868
  if (fileExists(settingsPath)) {
787
869
  try {
788
870
  const content = fs5.readFileSync(settingsPath, "utf-8");
789
871
  const parsed = JSON.parse(content);
790
872
  if (parsed && typeof parsed === "object") {
791
- settings = parsed;
873
+ userSettings = parsed;
792
874
  }
793
875
  } catch (error) {
794
876
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6 Gemini settings.json: ${error.message}`);
795
877
  }
796
878
  }
879
+ const settingsTemplate = loadGeminiSettingsTemplate();
880
+ const settings = deepMerge(settingsTemplate, userSettings);
797
881
  if (!settings.ide || typeof settings.ide !== "object") {
798
882
  settings.ide = {};
799
883
  }
@@ -818,15 +902,16 @@ function writeGeminiConfig(provider) {
818
902
  } catch (error) {
819
903
  throw new Error(`\u5199\u5165 Gemini settings.json \u5931\u8D25: ${error.message}`);
820
904
  }
821
- const env = loadEnvFile(envPath);
822
- if (provider.baseUrl && provider.baseUrl.trim().length > 0) {
823
- env.GOOGLE_GEMINI_BASE_URL = provider.baseUrl;
824
- } else {
905
+ const existingEnv = loadEnvFile(envPath);
906
+ const templateEnv = loadGeminiEnvTemplate(provider);
907
+ const env = {
908
+ ...existingEnv,
909
+ ...templateEnv
910
+ };
911
+ if (!templateEnv.GOOGLE_GEMINI_BASE_URL) {
825
912
  delete env.GOOGLE_GEMINI_BASE_URL;
826
913
  }
827
- if (provider.apiKey && provider.apiKey.trim().length > 0) {
828
- env.GEMINI_API_KEY = provider.apiKey;
829
- } else {
914
+ if (!templateEnv.GEMINI_API_KEY) {
830
915
  delete env.GEMINI_API_KEY;
831
916
  }
832
917
  let modelMeta = null;
@@ -854,26 +939,45 @@ function writeGeminiConfig(provider) {
854
939
  }
855
940
  saveEnvFile(envPath, env);
856
941
  }
942
+ var __filename3, __dirname3, GEMINI_SETTINGS_TEMPLATE, GEMINI_ENV_TEMPLATE;
857
943
  var init_gemini2 = __esm({
858
944
  "../core/dist/writers/gemini.js"() {
859
945
  "use strict";
860
946
  init_paths();
861
947
  init_file();
948
+ init_template();
949
+ __filename3 = fileURLToPath3(import.meta.url);
950
+ __dirname3 = path6.dirname(__filename3);
951
+ GEMINI_SETTINGS_TEMPLATE = {
952
+ ide: {
953
+ enabled: true
954
+ },
955
+ security: {
956
+ auth: {
957
+ selectedType: "gemini-api-key"
958
+ }
959
+ }
960
+ };
961
+ GEMINI_ENV_TEMPLATE = [
962
+ "# Managed by ccman",
963
+ "GOOGLE_GEMINI_BASE_URL={{baseUrl}}",
964
+ "GEMINI_API_KEY={{apiKey}}"
965
+ ].join("\n");
862
966
  }
863
967
  });
864
968
 
865
969
  // ../core/dist/writers/opencode.js
866
970
  import * as fs6 from "fs";
867
- import * as path6 from "path";
868
- import { fileURLToPath as fileURLToPath3 } from "url";
869
- function resolveTemplatePath3(relativePath) {
971
+ import * as path7 from "path";
972
+ import { fileURLToPath as fileURLToPath4 } from "url";
973
+ function resolveTemplatePath4(relativePath) {
870
974
  const candidates = [
871
975
  // @ccman/core runtime (dist/writers -> templates)
872
- path6.resolve(__dirname3, "../../templates", relativePath),
976
+ path7.resolve(__dirname4, "../../templates", relativePath),
873
977
  // Bundled CLI runtime (dist -> dist/templates)
874
- path6.resolve(__dirname3, "templates", relativePath),
978
+ path7.resolve(__dirname4, "templates", relativePath),
875
979
  // Fallback (some bundlers/layouts)
876
- path6.resolve(__dirname3, "../templates", relativePath)
980
+ path7.resolve(__dirname4, "../templates", relativePath)
877
981
  ];
878
982
  for (const candidate of candidates) {
879
983
  if (fs6.existsSync(candidate))
@@ -883,7 +987,7 @@ function resolveTemplatePath3(relativePath) {
883
987
  }
884
988
  function loadOpenCodeTemplateConfig() {
885
989
  try {
886
- const templatePath = resolveTemplatePath3("opencode/opencode.json");
990
+ const templatePath = resolveTemplatePath4("opencode/opencode.json");
887
991
  if (templatePath) {
888
992
  const content = fs6.readFileSync(templatePath, "utf-8");
889
993
  const parsed = JSON.parse(content);
@@ -975,7 +1079,7 @@ function writeOpenCodeConfig(provider) {
975
1079
  };
976
1080
  writeJSON(configPath, nextConfig);
977
1081
  }
978
- var OPENCODE_SCHEMA, OPENCODE_PROVIDER_KEY, OPENCODE_MODEL, OPENCODE_MODEL_KEY, __filename3, __dirname3, DEFAULT_MODELS, OPENCODE_CONFIG_TEMPLATE;
1082
+ var OPENCODE_SCHEMA, OPENCODE_PROVIDER_KEY, OPENCODE_MODEL, OPENCODE_MODEL_KEY, __filename4, __dirname4, DEFAULT_MODELS, OPENCODE_CONFIG_TEMPLATE;
979
1083
  var init_opencode2 = __esm({
980
1084
  "../core/dist/writers/opencode.js"() {
981
1085
  "use strict";
@@ -986,8 +1090,8 @@ var init_opencode2 = __esm({
986
1090
  OPENCODE_PROVIDER_KEY = "openai";
987
1091
  OPENCODE_MODEL = "openai/gpt-5.2-codex";
988
1092
  OPENCODE_MODEL_KEY = "gpt-5.2-codex";
989
- __filename3 = fileURLToPath3(import.meta.url);
990
- __dirname3 = path6.dirname(__filename3);
1093
+ __filename4 = fileURLToPath4(import.meta.url);
1094
+ __dirname4 = path7.dirname(__filename4);
991
1095
  DEFAULT_MODELS = {
992
1096
  [OPENCODE_MODEL_KEY]: {
993
1097
  name: "GPT-5.2 Codex",
@@ -1057,7 +1161,7 @@ var init_tool_manager_types = __esm({
1057
1161
  });
1058
1162
 
1059
1163
  // ../core/dist/tool-manager.js
1060
- import * as path7 from "path";
1164
+ import * as path8 from "path";
1061
1165
  function createToolManager(tool) {
1062
1166
  const toolConfig = TOOL_CONFIGS[tool];
1063
1167
  const configPath = toolConfig.configPath;
@@ -1088,21 +1192,50 @@ function createToolManager(tool) {
1088
1192
  }
1089
1193
  writeJSON(configPath, config);
1090
1194
  }
1195
+ function trimInput(value) {
1196
+ if (value === void 0)
1197
+ return void 0;
1198
+ return value.trim();
1199
+ }
1200
+ function trimProvider(input) {
1201
+ return {
1202
+ ...input,
1203
+ name: input.name.trim(),
1204
+ desc: trimInput(input.desc),
1205
+ baseUrl: input.baseUrl.trim(),
1206
+ apiKey: input.apiKey.trim(),
1207
+ model: trimInput(input.model)
1208
+ };
1209
+ }
1210
+ function trimProviderUpdates(updates) {
1211
+ return {
1212
+ ...updates,
1213
+ name: trimInput(updates.name),
1214
+ desc: trimInput(updates.desc),
1215
+ baseUrl: trimInput(updates.baseUrl),
1216
+ apiKey: trimInput(updates.apiKey),
1217
+ model: trimInput(updates.model)
1218
+ };
1219
+ }
1091
1220
  return {
1092
1221
  add(input) {
1093
1222
  const config = loadConfig2();
1094
- const nameExists = config.providers.some((p) => p.name === input.name);
1223
+ const normalizedInput = trimProvider(input);
1224
+ if (!normalizedInput.name) {
1225
+ throw new Error("\u670D\u52A1\u5546\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
1226
+ }
1227
+ const nameExists = config.providers.some((p) => p.name.trim() === normalizedInput.name);
1095
1228
  if (nameExists) {
1096
- throw new ProviderNameConflictError(input.name);
1229
+ throw new ProviderNameConflictError(normalizedInput.name);
1097
1230
  }
1098
1231
  const timestamp = Date.now();
1099
1232
  const provider = {
1100
1233
  id: generateId(),
1101
- name: input.name,
1102
- desc: input.desc,
1103
- baseUrl: input.baseUrl,
1104
- apiKey: input.apiKey,
1105
- model: input.model,
1234
+ name: normalizedInput.name,
1235
+ desc: normalizedInput.desc,
1236
+ baseUrl: normalizedInput.baseUrl,
1237
+ apiKey: normalizedInput.apiKey,
1238
+ model: normalizedInput.model,
1106
1239
  createdAt: timestamp,
1107
1240
  lastModified: timestamp
1108
1241
  };
@@ -1127,8 +1260,10 @@ function createToolManager(tool) {
1127
1260
  },
1128
1261
  findByName(name) {
1129
1262
  const config = loadConfig2();
1130
- const lowerName = name.toLowerCase();
1131
- return config.providers.find((p) => p.name.toLowerCase() === lowerName);
1263
+ const lowerName = name.trim().toLowerCase();
1264
+ if (!lowerName)
1265
+ return void 0;
1266
+ return config.providers.find((p) => p.name.trim().toLowerCase() === lowerName);
1132
1267
  },
1133
1268
  switch(id) {
1134
1269
  const config = loadConfig2();
@@ -1155,25 +1290,29 @@ function createToolManager(tool) {
1155
1290
  edit(id, updates) {
1156
1291
  const config = loadConfig2();
1157
1292
  const provider = config.providers.find((p) => p.id === id);
1293
+ const normalizedUpdates = trimProviderUpdates(updates);
1158
1294
  if (!provider) {
1159
1295
  throw new ProviderNotFoundError(id);
1160
1296
  }
1161
- if (updates.name !== void 0 && updates.name !== provider.name) {
1162
- const nameConflict = config.providers.some((p) => p.id !== id && p.name === updates.name);
1297
+ if (normalizedUpdates.name !== void 0 && !normalizedUpdates.name) {
1298
+ throw new Error("\u670D\u52A1\u5546\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
1299
+ }
1300
+ if (normalizedUpdates.name !== void 0 && normalizedUpdates.name !== provider.name.trim()) {
1301
+ const nameConflict = config.providers.some((p) => p.id !== id && p.name.trim() === normalizedUpdates.name);
1163
1302
  if (nameConflict) {
1164
- throw new ProviderNameConflictError(updates.name);
1303
+ throw new ProviderNameConflictError(normalizedUpdates.name);
1165
1304
  }
1166
1305
  }
1167
- if (updates.name !== void 0)
1168
- provider.name = updates.name;
1169
- if (updates.desc !== void 0)
1170
- provider.desc = updates.desc;
1171
- if (updates.baseUrl !== void 0)
1172
- provider.baseUrl = updates.baseUrl;
1173
- if (updates.apiKey !== void 0)
1174
- provider.apiKey = updates.apiKey;
1175
- if (updates.model !== void 0)
1176
- provider.model = updates.model;
1306
+ if (normalizedUpdates.name !== void 0)
1307
+ provider.name = normalizedUpdates.name;
1308
+ if (normalizedUpdates.desc !== void 0)
1309
+ provider.desc = normalizedUpdates.desc;
1310
+ if (normalizedUpdates.baseUrl !== void 0)
1311
+ provider.baseUrl = normalizedUpdates.baseUrl;
1312
+ if (normalizedUpdates.apiKey !== void 0)
1313
+ provider.apiKey = normalizedUpdates.apiKey;
1314
+ if (normalizedUpdates.model !== void 0)
1315
+ provider.model = normalizedUpdates.model;
1177
1316
  provider.lastModified = Date.now();
1178
1317
  saveConfig2(config);
1179
1318
  if (config.currentProviderId === id) {
@@ -1202,15 +1341,19 @@ function createToolManager(tool) {
1202
1341
  clone(sourceId, newName) {
1203
1342
  const source = this.get(sourceId);
1204
1343
  const config = loadConfig2();
1205
- const nameExists = config.providers.some((p) => p.name === newName);
1344
+ const normalizedName = newName.trim();
1345
+ if (!normalizedName) {
1346
+ throw new Error("\u670D\u52A1\u5546\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
1347
+ }
1348
+ const nameExists = config.providers.some((p) => p.name.trim() === normalizedName);
1206
1349
  if (nameExists) {
1207
- throw new ProviderNameConflictError(newName);
1350
+ throw new ProviderNameConflictError(normalizedName);
1208
1351
  }
1209
1352
  const timestamp = Date.now();
1210
1353
  const newProvider = {
1211
1354
  ...source,
1212
1355
  id: generateId(),
1213
- name: newName,
1356
+ name: normalizedName,
1214
1357
  desc: void 0,
1215
1358
  createdAt: timestamp,
1216
1359
  lastModified: timestamp,
@@ -1222,18 +1365,27 @@ function createToolManager(tool) {
1222
1365
  },
1223
1366
  addPreset(input) {
1224
1367
  const config = loadConfig2();
1368
+ const normalizedInput = {
1369
+ ...input,
1370
+ name: input.name.trim(),
1371
+ baseUrl: input.baseUrl.trim(),
1372
+ description: input.description.trim()
1373
+ };
1374
+ if (!normalizedInput.name) {
1375
+ throw new Error("\u9884\u7F6E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
1376
+ }
1225
1377
  if (!config.presets) {
1226
1378
  config.presets = [];
1227
1379
  }
1228
1380
  const allPresets = [...toolConfig.builtinPresets, ...config.presets];
1229
- const nameExists = allPresets.some((p) => p.name === input.name);
1381
+ const nameExists = allPresets.some((p) => p.name.trim() === normalizedInput.name);
1230
1382
  if (nameExists) {
1231
- throw new PresetNameConflictError(input.name);
1383
+ throw new PresetNameConflictError(normalizedInput.name);
1232
1384
  }
1233
1385
  const preset = {
1234
- name: input.name,
1235
- baseUrl: input.baseUrl,
1236
- description: input.description
1386
+ name: normalizedInput.name,
1387
+ baseUrl: normalizedInput.baseUrl,
1388
+ description: normalizedInput.description
1237
1389
  };
1238
1390
  config.presets.push(preset);
1239
1391
  saveConfig2(config);
@@ -1257,26 +1409,36 @@ function createToolManager(tool) {
1257
1409
  },
1258
1410
  editPreset(name, updates) {
1259
1411
  const config = loadConfig2();
1412
+ const normalizedName = name.trim();
1413
+ const normalizedUpdates = {
1414
+ ...updates,
1415
+ name: trimInput(updates.name),
1416
+ baseUrl: trimInput(updates.baseUrl),
1417
+ description: trimInput(updates.description)
1418
+ };
1260
1419
  if (!config.presets) {
1261
1420
  config.presets = [];
1262
1421
  }
1263
- const preset = config.presets.find((p) => p.name === name);
1422
+ const preset = config.presets.find((p) => p.name.trim() === normalizedName);
1264
1423
  if (!preset) {
1265
- throw new Error(`\u9884\u7F6E\u4E0D\u5B58\u5728: ${name}`);
1424
+ throw new Error(`\u9884\u7F6E\u4E0D\u5B58\u5728: ${normalizedName}`);
1266
1425
  }
1267
- if (updates.name !== void 0 && updates.name !== preset.name) {
1426
+ if (normalizedUpdates.name !== void 0 && !normalizedUpdates.name) {
1427
+ throw new Error("\u9884\u7F6E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
1428
+ }
1429
+ if (normalizedUpdates.name !== void 0 && normalizedUpdates.name !== preset.name.trim()) {
1268
1430
  const allPresets = [...toolConfig.builtinPresets, ...config.presets];
1269
- const nameConflict = allPresets.some((p) => p.name !== name && p.name === updates.name);
1431
+ const nameConflict = allPresets.some((p) => p.name.trim() !== normalizedName && p.name.trim() === normalizedUpdates.name);
1270
1432
  if (nameConflict) {
1271
- throw new PresetNameConflictError(updates.name);
1433
+ throw new PresetNameConflictError(normalizedUpdates.name);
1272
1434
  }
1273
1435
  }
1274
- if (updates.name !== void 0)
1275
- preset.name = updates.name;
1276
- if (updates.baseUrl !== void 0)
1277
- preset.baseUrl = updates.baseUrl;
1278
- if (updates.description !== void 0)
1279
- preset.description = updates.description;
1436
+ if (normalizedUpdates.name !== void 0)
1437
+ preset.name = normalizedUpdates.name;
1438
+ if (normalizedUpdates.baseUrl !== void 0)
1439
+ preset.baseUrl = normalizedUpdates.baseUrl;
1440
+ if (normalizedUpdates.description !== void 0)
1441
+ preset.description = normalizedUpdates.description;
1280
1442
  saveConfig2(config);
1281
1443
  return {
1282
1444
  ...preset,
@@ -1285,12 +1447,13 @@ function createToolManager(tool) {
1285
1447
  },
1286
1448
  removePreset(name) {
1287
1449
  const config = loadConfig2();
1450
+ const normalizedName = name.trim();
1288
1451
  if (!config.presets) {
1289
1452
  return;
1290
1453
  }
1291
- const index = config.presets.findIndex((p) => p.name === name);
1454
+ const index = config.presets.findIndex((p) => p.name.trim() === normalizedName);
1292
1455
  if (index === -1) {
1293
- throw new Error(`Preset not found: ${name}`);
1456
+ throw new Error(`Preset not found: ${normalizedName}`);
1294
1457
  }
1295
1458
  config.presets.splice(index, 1);
1296
1459
  saveConfig2(config);
@@ -1331,17 +1494,17 @@ var init_tool_manager = __esm({
1331
1494
  init_tool_manager_types();
1332
1495
  TOOL_CONFIGS = {
1333
1496
  codex: {
1334
- configPath: path7.join(getCcmanDir(), "codex.json"),
1497
+ configPath: path8.join(getCcmanDir(), "codex.json"),
1335
1498
  builtinPresets: CODEX_PRESETS,
1336
1499
  writer: writeCodexConfig
1337
1500
  },
1338
1501
  claude: {
1339
- configPath: path7.join(getCcmanDir(), "claude.json"),
1502
+ configPath: path8.join(getCcmanDir(), "claude.json"),
1340
1503
  builtinPresets: CC_PRESETS,
1341
1504
  writer: writeClaudeConfig
1342
1505
  },
1343
1506
  mcp: {
1344
- configPath: path7.join(getCcmanDir(), "mcp.json"),
1507
+ configPath: path8.join(getCcmanDir(), "mcp.json"),
1345
1508
  builtinPresets: MCP_PRESETS,
1346
1509
  writer: writeMCPConfig,
1347
1510
  autoSync: true,
@@ -1371,12 +1534,12 @@ var init_tool_manager = __esm({
1371
1534
  }
1372
1535
  },
1373
1536
  gemini: {
1374
- configPath: path7.join(getCcmanDir(), "gemini.json"),
1537
+ configPath: path8.join(getCcmanDir(), "gemini.json"),
1375
1538
  builtinPresets: GEMINI_PRESETS,
1376
1539
  writer: writeGeminiConfig
1377
1540
  },
1378
1541
  opencode: {
1379
- configPath: path7.join(getCcmanDir(), "opencode.json"),
1542
+ configPath: path8.join(getCcmanDir(), "opencode.json"),
1380
1543
  builtinPresets: OPENCODE_PRESETS,
1381
1544
  writer: writeOpenCodeConfig
1382
1545
  }
@@ -1430,8 +1593,16 @@ function getSyncConfig() {
1430
1593
  return config.sync || null;
1431
1594
  }
1432
1595
  function saveSyncConfig(syncConfig) {
1596
+ const normalizedSyncConfig = {
1597
+ ...syncConfig,
1598
+ webdavUrl: syncConfig.webdavUrl.trim(),
1599
+ username: syncConfig.username.trim(),
1600
+ password: syncConfig.password.trim(),
1601
+ remoteDir: syncConfig.remoteDir?.trim(),
1602
+ syncPassword: syncConfig.syncPassword?.trim()
1603
+ };
1433
1604
  const config = loadConfig();
1434
- config.sync = syncConfig;
1605
+ config.sync = normalizedSyncConfig;
1435
1606
  saveConfig(config);
1436
1607
  }
1437
1608
  function updateLastSyncTime() {
@@ -1466,9 +1637,9 @@ function joinPath(baseDir, filename) {
1466
1637
  return `${normalizedBase}/${normalizedFile}`;
1467
1638
  }
1468
1639
  function createWebDAVClient(config) {
1469
- const client = createClient(config.webdavUrl, {
1470
- username: config.username,
1471
- password: config.password,
1640
+ const client = createClient(config.webdavUrl.trim(), {
1641
+ username: config.username.trim(),
1642
+ password: config.password.trim(),
1472
1643
  authType: config.authType || "password",
1473
1644
  // 默认使用 Basic Auth
1474
1645
  maxBodyLength: 100 * 1024 * 1024,
@@ -1745,7 +1916,7 @@ var init_merge_advanced = __esm({
1745
1916
 
1746
1917
  // ../core/dist/sync/merge.js
1747
1918
  import fs8 from "fs";
1748
- import path8 from "path";
1919
+ import path9 from "path";
1749
1920
  function backupConfig(configPath, keepCount = 3) {
1750
1921
  if (!fs8.existsSync(configPath)) {
1751
1922
  throw new Error(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${configPath}`);
@@ -1757,8 +1928,8 @@ function backupConfig(configPath, keepCount = 3) {
1757
1928
  return backupPath;
1758
1929
  }
1759
1930
  function cleanupOldBackups(configPath, keepCount) {
1760
- const dir = path8.dirname(configPath);
1761
- const basename = path8.basename(configPath);
1931
+ const dir = path9.dirname(configPath);
1932
+ const basename = path9.basename(configPath);
1762
1933
  const backupPrefix = `${basename}.backup.`;
1763
1934
  try {
1764
1935
  const files = fs8.readdirSync(dir);
@@ -1770,7 +1941,7 @@ function cleanupOldBackups(configPath, keepCount) {
1770
1941
  }
1771
1942
  return {
1772
1943
  name: f,
1773
- path: path8.join(dir, f),
1944
+ path: path9.join(dir, f),
1774
1945
  timestamp
1775
1946
  };
1776
1947
  }).filter((backup) => backup !== null).sort((a, b) => b.timestamp - a.timestamp);
@@ -1794,13 +1965,13 @@ var init_merge = __esm({
1794
1965
 
1795
1966
  // ../core/dist/sync/sync-v2.js
1796
1967
  import fs9 from "fs";
1797
- import path9 from "path";
1968
+ import path10 from "path";
1798
1969
  async function uploadToCloud(config, password) {
1799
1970
  const ccmanDir2 = getCcmanDir();
1800
1971
  const toolKeys = Object.keys(TOOL_SYNC_CONFIG);
1801
1972
  for (const tool of toolKeys) {
1802
1973
  const { remotePath, configFilename } = TOOL_SYNC_CONFIG[tool];
1803
- const configPath = path9.join(ccmanDir2, configFilename);
1974
+ const configPath = path10.join(ccmanDir2, configFilename);
1804
1975
  const localConfig = readJSON(configPath);
1805
1976
  const encryptedProviders = encryptProviders(localConfig.providers, password);
1806
1977
  const encryptedConfig = {
@@ -1846,7 +2017,7 @@ async function downloadFromCloud(config, password) {
1846
2017
  try {
1847
2018
  for (const tool of toolKeys) {
1848
2019
  const { configFilename } = TOOL_SYNC_CONFIG[tool];
1849
- const configPath = path9.join(ccmanDir2, configFilename);
2020
+ const configPath = path10.join(ccmanDir2, configFilename);
1850
2021
  if (fs9.existsSync(configPath)) {
1851
2022
  backupPaths.push(backupConfig(configPath));
1852
2023
  }
@@ -1859,7 +2030,7 @@ async function downloadFromCloud(config, password) {
1859
2030
  if (!remoteConfig || !decryptedProviders)
1860
2031
  continue;
1861
2032
  const { configFilename } = TOOL_SYNC_CONFIG[tool];
1862
- const configPath = path9.join(ccmanDir2, configFilename);
2033
+ const configPath = path10.join(ccmanDir2, configFilename);
1863
2034
  const newConfig = {
1864
2035
  ...remoteConfig,
1865
2036
  // 使用云端配置的所有字段
@@ -1901,7 +2072,7 @@ async function mergeSync(config, password) {
1901
2072
  for (let i = 0; i < toolKeys.length; i++) {
1902
2073
  const tool = toolKeys[i];
1903
2074
  const { remotePath, configFilename } = TOOL_SYNC_CONFIG[tool];
1904
- const configPath = path9.join(ccmanDir2, configFilename);
2075
+ const configPath = path10.join(ccmanDir2, configFilename);
1905
2076
  const localConfig = readJSON(configPath);
1906
2077
  let remoteProviders = [];
1907
2078
  if (existsChecks[i]) {
@@ -1933,7 +2104,7 @@ async function mergeSync(config, password) {
1933
2104
  try {
1934
2105
  for (const tool of toolKeys) {
1935
2106
  const { configFilename } = TOOL_SYNC_CONFIG[tool];
1936
- const configPath = path9.join(ccmanDir2, configFilename);
2107
+ const configPath = path10.join(ccmanDir2, configFilename);
1937
2108
  if (fs9.existsSync(configPath)) {
1938
2109
  backupPaths.push(backupConfig(configPath));
1939
2110
  }
@@ -1945,7 +2116,7 @@ async function mergeSync(config, password) {
1945
2116
  for (let i = 0; i < mergeDataList.length; i++) {
1946
2117
  const { tool, localConfig, mergeResult } = mergeDataList[i];
1947
2118
  const { remotePath, configFilename } = TOOL_SYNC_CONFIG[tool];
1948
- const configPath = path9.join(ccmanDir2, configFilename);
2119
+ const configPath = path10.join(ccmanDir2, configFilename);
1949
2120
  let remoteConfig = null;
1950
2121
  if (existsChecks[i]) {
1951
2122
  const jsonContent2 = await downloadFromWebDAV(config, remotePath);
@@ -2036,11 +2207,11 @@ var init_sync_v2 = __esm({
2036
2207
 
2037
2208
  // ../core/dist/export.js
2038
2209
  import * as fs10 from "fs";
2039
- import * as path10 from "path";
2210
+ import * as path11 from "path";
2040
2211
  function validateExport() {
2041
2212
  const ccmanDir2 = getCcmanDir();
2042
- const codexPath = path10.join(ccmanDir2, CODEX_CONFIG_FILE);
2043
- const claudePath = path10.join(ccmanDir2, CLAUDE_CONFIG_FILE);
2213
+ const codexPath = path11.join(ccmanDir2, CODEX_CONFIG_FILE);
2214
+ const claudePath = path11.join(ccmanDir2, CLAUDE_CONFIG_FILE);
2044
2215
  const missingFiles = [];
2045
2216
  if (!fileExists(codexPath)) {
2046
2217
  missingFiles.push(CODEX_CONFIG_FILE);
@@ -2073,8 +2244,8 @@ function validateImportDir(sourceDir) {
2073
2244
  foundFiles: []
2074
2245
  };
2075
2246
  }
2076
- const codexPath = path10.join(sourceDir, CODEX_CONFIG_FILE);
2077
- const claudePath = path10.join(sourceDir, CLAUDE_CONFIG_FILE);
2247
+ const codexPath = path11.join(sourceDir, CODEX_CONFIG_FILE);
2248
+ const claudePath = path11.join(sourceDir, CLAUDE_CONFIG_FILE);
2078
2249
  const foundFiles = [];
2079
2250
  if (fileExists(codexPath)) {
2080
2251
  foundFiles.push(CODEX_CONFIG_FILE);
@@ -2102,14 +2273,14 @@ function exportConfig(targetDir) {
2102
2273
  ensureDir(targetDir);
2103
2274
  const ccmanDir2 = getCcmanDir();
2104
2275
  const exportedFiles = [];
2105
- const codexSrc = path10.join(ccmanDir2, CODEX_CONFIG_FILE);
2106
- const codexDst = path10.join(targetDir, CODEX_CONFIG_FILE);
2276
+ const codexSrc = path11.join(ccmanDir2, CODEX_CONFIG_FILE);
2277
+ const codexDst = path11.join(targetDir, CODEX_CONFIG_FILE);
2107
2278
  if (fileExists(codexSrc)) {
2108
2279
  fs10.copyFileSync(codexSrc, codexDst);
2109
2280
  exportedFiles.push(CODEX_CONFIG_FILE);
2110
2281
  }
2111
- const claudeSrc = path10.join(ccmanDir2, CLAUDE_CONFIG_FILE);
2112
- const claudeDst = path10.join(targetDir, CLAUDE_CONFIG_FILE);
2282
+ const claudeSrc = path11.join(ccmanDir2, CLAUDE_CONFIG_FILE);
2283
+ const claudeDst = path11.join(targetDir, CLAUDE_CONFIG_FILE);
2113
2284
  if (fileExists(claudeSrc)) {
2114
2285
  fs10.copyFileSync(claudeSrc, claudeDst);
2115
2286
  exportedFiles.push(CLAUDE_CONFIG_FILE);
@@ -2131,22 +2302,22 @@ function importConfig(sourceDir) {
2131
2302
  ensureDir(ccmanDir2);
2132
2303
  try {
2133
2304
  if (validation.foundFiles.includes(CODEX_CONFIG_FILE)) {
2134
- const codexDst = path10.join(ccmanDir2, CODEX_CONFIG_FILE);
2305
+ const codexDst = path11.join(ccmanDir2, CODEX_CONFIG_FILE);
2135
2306
  if (fileExists(codexDst)) {
2136
2307
  const backupPath = backupConfig(codexDst);
2137
2308
  backupPaths.push(backupPath);
2138
2309
  }
2139
- const codexSrc = path10.join(sourceDir, CODEX_CONFIG_FILE);
2310
+ const codexSrc = path11.join(sourceDir, CODEX_CONFIG_FILE);
2140
2311
  fs10.copyFileSync(codexSrc, codexDst);
2141
2312
  importedFiles.push(CODEX_CONFIG_FILE);
2142
2313
  }
2143
2314
  if (validation.foundFiles.includes(CLAUDE_CONFIG_FILE)) {
2144
- const claudeDst = path10.join(ccmanDir2, CLAUDE_CONFIG_FILE);
2315
+ const claudeDst = path11.join(ccmanDir2, CLAUDE_CONFIG_FILE);
2145
2316
  if (fileExists(claudeDst)) {
2146
2317
  const backupPath = backupConfig(claudeDst);
2147
2318
  backupPaths.push(backupPath);
2148
2319
  }
2149
- const claudeSrc = path10.join(sourceDir, CLAUDE_CONFIG_FILE);
2320
+ const claudeSrc = path11.join(sourceDir, CLAUDE_CONFIG_FILE);
2150
2321
  fs10.copyFileSync(claudeSrc, claudeDst);
2151
2322
  importedFiles.push(CLAUDE_CONFIG_FILE);
2152
2323
  }
@@ -2819,8 +2990,8 @@ function downloadCommand(program2) {
2819
2990
  console.log();
2820
2991
  if (backupPaths.length > 0) {
2821
2992
  console.log(chalk7.gray("\u672C\u5730\u5907\u4EFD:"));
2822
- backupPaths.forEach((path13) => {
2823
- console.log(chalk7.gray(` ${path13}`));
2993
+ backupPaths.forEach((path14) => {
2994
+ console.log(chalk7.gray(` ${path14}`));
2824
2995
  });
2825
2996
  console.log();
2826
2997
  }
@@ -2881,8 +3052,8 @@ function mergeCommand(program2) {
2881
3052
  console.log();
2882
3053
  if (result.backupPaths.length > 0) {
2883
3054
  console.log(chalk8.gray("\u5907\u4EFD:"));
2884
- result.backupPaths.forEach((path13) => {
2885
- console.log(chalk8.gray(` ${path13}`));
3055
+ result.backupPaths.forEach((path14) => {
3056
+ console.log(chalk8.gray(` ${path14}`));
2886
3057
  });
2887
3058
  console.log();
2888
3059
  }
@@ -3159,7 +3330,7 @@ async function promptProviderForm(defaults) {
3159
3330
  message: "\u670D\u52A1\u5546\u540D\u79F0:",
3160
3331
  default: defaults?.name || void 0,
3161
3332
  validate: (value) => {
3162
- if (!value) return "\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
3333
+ if (!value?.trim()) return "\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
3163
3334
  return true;
3164
3335
  }
3165
3336
  },
@@ -3175,8 +3346,9 @@ async function promptProviderForm(defaults) {
3175
3346
  message: "API \u5730\u5740:",
3176
3347
  default: defaults?.baseUrl || void 0,
3177
3348
  validate: (value) => {
3178
- if (!value) return "API \u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A";
3179
- if (!value.startsWith("http://") && !value.startsWith("https://")) {
3349
+ const trimmed = value?.trim();
3350
+ if (!trimmed) return "API \u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A";
3351
+ if (!trimmed.startsWith("http://") && !trimmed.startsWith("https://")) {
3180
3352
  return "API \u5730\u5740\u5FC5\u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934";
3181
3353
  }
3182
3354
  return true;
@@ -3189,16 +3361,16 @@ async function promptProviderForm(defaults) {
3189
3361
  default: defaults?.apiKey || void 0,
3190
3362
  mask: "*",
3191
3363
  validate: (value) => {
3192
- if (!value) return "API \u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A";
3364
+ if (!value?.trim()) return "API \u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A";
3193
3365
  return true;
3194
3366
  }
3195
3367
  }
3196
3368
  ]);
3197
3369
  return {
3198
- name: answers.name,
3199
- desc: answers.desc || void 0,
3200
- baseUrl: answers.baseUrl,
3201
- apiKey: answers.apiKey
3370
+ name: answers.name.trim(),
3371
+ desc: answers.desc?.trim() || void 0,
3372
+ baseUrl: answers.baseUrl.trim(),
3373
+ apiKey: answers.apiKey.trim()
3202
3374
  };
3203
3375
  }
3204
3376
  async function startMainMenu() {
@@ -6075,7 +6247,7 @@ init_sync();
6075
6247
  // src/commands/export.ts
6076
6248
  init_dist2();
6077
6249
  import chalk45 from "chalk";
6078
- import path11 from "path";
6250
+ import path12 from "path";
6079
6251
  function exportCommand(program2) {
6080
6252
  program2.command("export <\u76EE\u6807\u76EE\u5F55>").description("\u5BFC\u51FA\u914D\u7F6E\u5230\u672C\u5730\u76EE\u5F55\uFF08\u5305\u542B API Key\uFF09").action(async (targetDir) => {
6081
6253
  try {
@@ -6086,7 +6258,7 @@ function exportCommand(program2) {
6086
6258
  `));
6087
6259
  process.exit(1);
6088
6260
  }
6089
- const resolvedPath = targetDir.startsWith("~") ? path11.join(process.env.HOME || "", targetDir.slice(1)) : path11.resolve(targetDir);
6261
+ const resolvedPath = targetDir.startsWith("~") ? path12.join(process.env.HOME || "", targetDir.slice(1)) : path12.resolve(targetDir);
6090
6262
  console.log("\u5BFC\u51FA\u6587\u4EF6:");
6091
6263
  console.log(` ${chalk45.cyan("codex.json")} - Codex \u914D\u7F6E`);
6092
6264
  console.log(` ${chalk45.cyan("claude.json")} - Claude \u914D\u7F6E`);
@@ -6118,11 +6290,11 @@ function exportCommand(program2) {
6118
6290
  init_dist2();
6119
6291
  import chalk46 from "chalk";
6120
6292
  import inquirer32 from "inquirer";
6121
- import path12 from "path";
6293
+ import path13 from "path";
6122
6294
  function importCommand(program2) {
6123
6295
  program2.command("import <\u6E90\u76EE\u5F55>").description("\u4ECE\u672C\u5730\u76EE\u5F55\u5BFC\u5165\u914D\u7F6E\uFF08\u4F1A\u8986\u76D6\u5F53\u524D\u914D\u7F6E\uFF09").action(async (sourceDir) => {
6124
6296
  try {
6125
- const resolvedPath = sourceDir.startsWith("~") ? path12.join(process.env.HOME || "", sourceDir.slice(1)) : path12.resolve(sourceDir);
6297
+ const resolvedPath = sourceDir.startsWith("~") ? path13.join(process.env.HOME || "", sourceDir.slice(1)) : path13.resolve(sourceDir);
6126
6298
  console.log(chalk46.bold("\n\u{1F4E5} \u5BFC\u5165\u914D\u7F6E\n"));
6127
6299
  const validation = validateImportDir(resolvedPath);
6128
6300
  if (!validation.valid) {
@@ -6288,12 +6460,12 @@ async function promptApiKey() {
6288
6460
  message: "\u8BF7\u8F93\u5165 GMN API Key:",
6289
6461
  mask: "*",
6290
6462
  validate: (value) => {
6291
- if (!value) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
6463
+ if (!value?.trim()) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
6292
6464
  return true;
6293
6465
  }
6294
6466
  }
6295
6467
  ]);
6296
- return answers.apiKey;
6468
+ return answers.apiKey.trim();
6297
6469
  }
6298
6470
  async function promptPlatforms() {
6299
6471
  const answers = await inquirer33.prompt([
@@ -1,13 +1,50 @@
1
- model_provider = "gmn"
2
1
  model = "gpt-5.3-codex"
3
2
  model_reasoning_effort = "xhigh"
4
- network_access = "enabled"
5
3
  disable_response_storage = true
4
+ sandbox_mode = "danger-full-access"
6
5
  windows_wsl_setup_acknowledged = true
7
- model_verbosity = "high"
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
8
45
 
9
46
  [model_providers.gmn]
10
47
  name = "gmn"
11
48
  base_url = "https://gmn.chuangzuoli.com"
12
49
  wire_api = "responses"
13
- requires_openai_auth = true
50
+ requires_openai_auth = true
@@ -0,0 +1,10 @@
1
+ {
2
+ "ide": {
3
+ "enabled": true
4
+ },
5
+ "security": {
6
+ "auth": {
7
+ "selectedType": "gemini-api-key"
8
+ }
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccman",
3
- "version": "3.3.10",
3
+ "version": "3.3.12",
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",