ccman 3.3.17 → 3.3.19

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
@@ -9,69 +9,6 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
- // ../core/package.json
13
- var package_default;
14
- var init_package = __esm({
15
- "../core/package.json"() {
16
- package_default = {
17
- name: "@ccman/core",
18
- version: "3.3.17",
19
- type: "module",
20
- description: "Core business logic for ccman - Manage Codex, Claude Code, Gemini CLI, OpenCode, OpenClaw, and MCP configurations",
21
- main: "./dist/index.js",
22
- types: "./dist/index.d.ts",
23
- files: [
24
- "dist",
25
- "templates"
26
- ],
27
- scripts: {
28
- build: "tsc",
29
- test: "vitest run",
30
- "test:watch": "vitest",
31
- clean: "rm -rf dist"
32
- },
33
- keywords: [
34
- "codex",
35
- "claude",
36
- "claude-code",
37
- "gemini",
38
- "gemini-cli",
39
- "opencode",
40
- "openclaw",
41
- "mcp",
42
- "model-context-protocol",
43
- "ai",
44
- "api",
45
- "config",
46
- "manager"
47
- ],
48
- author: "2ue",
49
- license: "MIT",
50
- repository: {
51
- type: "git",
52
- url: "https://github.com/2ue/ccman.git",
53
- directory: "packages/core"
54
- },
55
- homepage: "https://github.com/2ue/ccman#readme",
56
- bugs: {
57
- url: "https://github.com/2ue/ccman/issues"
58
- },
59
- dependencies: {
60
- "@ccman/types": "workspace:*",
61
- "@iarna/toml": "^2.2.5",
62
- "proper-lockfile": "^4.1.2",
63
- webdav: "^5.8.0"
64
- },
65
- devDependencies: {
66
- vitest: "^1.0.0"
67
- },
68
- engines: {
69
- node: ">=18.0.0"
70
- }
71
- };
72
- }
73
- });
74
-
75
12
  // ../types/dist/index.js
76
13
  var TOOL_TYPES, MAIN_TOOL_TYPES, TOOL_CONFIG;
77
14
  var init_dist = __esm({
@@ -273,6 +210,35 @@ var init_file = __esm({
273
210
  }
274
211
  });
275
212
 
213
+ // ../core/dist/utils/template.js
214
+ function replaceVariables(template, variables) {
215
+ const jsonStr = JSON.stringify(template);
216
+ let result = jsonStr;
217
+ for (const [key, value] of Object.entries(variables)) {
218
+ const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
219
+ result = result.replace(new RegExp(`{{${key}}}`, "g"), escapedValue);
220
+ }
221
+ return JSON.parse(result);
222
+ }
223
+ function deepMerge(target, source) {
224
+ const result = { ...target };
225
+ for (const key in source) {
226
+ const sourceValue = source[key];
227
+ const targetValue = result[key];
228
+ if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
229
+ result[key] = deepMerge(targetValue, sourceValue);
230
+ } else {
231
+ result[key] = sourceValue;
232
+ }
233
+ }
234
+ return result;
235
+ }
236
+ var init_template = __esm({
237
+ "../core/dist/utils/template.js"() {
238
+ "use strict";
239
+ }
240
+ });
241
+
276
242
  // ../core/dist/writers/codex.js
277
243
  import * as fs2 from "fs";
278
244
  import * as path3 from "path";
@@ -295,7 +261,7 @@ function resolveTemplatePath(relativePath) {
295
261
  }
296
262
  function resolveCodexProviderKey(provider) {
297
263
  const baseUrl = (provider.baseUrl || "").toLowerCase();
298
- if (baseUrl.includes("gmn.chuangzuoli.com"))
264
+ if (GMN_PROVIDER_HOSTS.some((host) => baseUrl.includes(host)))
299
265
  return "gmn";
300
266
  return provider.name;
301
267
  }
@@ -310,43 +276,105 @@ function loadCodexTemplateConfig() {
310
276
  }
311
277
  return CODEX_DEFAULT_CONFIG;
312
278
  }
313
- function writeCodexConfig(provider) {
279
+ function removeDeprecatedKeys(config) {
280
+ if (config.features && typeof config.features === "object" && !Array.isArray(config.features) && "web_search_request" in config.features) {
281
+ delete config.features.web_search_request;
282
+ }
283
+ if ("web_search_request" in config) {
284
+ delete config.web_search_request;
285
+ }
286
+ }
287
+ function loadExistingCodexConfig(configPath) {
288
+ if (!fileExists(configPath)) {
289
+ return {};
290
+ }
291
+ try {
292
+ const content = fs2.readFileSync(configPath, "utf-8");
293
+ return parseToml(content);
294
+ } catch {
295
+ return {};
296
+ }
297
+ }
298
+ function buildManagedProvider(provider, providerKey) {
299
+ return {
300
+ name: providerKey,
301
+ base_url: provider.baseUrl,
302
+ wire_api: "responses",
303
+ requires_openai_auth: true
304
+ };
305
+ }
306
+ function writeCodexConfigOverwrite(provider) {
314
307
  ensureDir(getCodexDir());
315
308
  const configPath = getCodexConfigPath();
316
309
  const templateConfig = loadCodexTemplateConfig();
317
310
  const nextConfig = { ...templateConfig };
318
- if (nextConfig.features && typeof nextConfig.features === "object" && !Array.isArray(nextConfig.features) && "web_search_request" in nextConfig.features) {
319
- delete nextConfig.features.web_search_request;
320
- }
321
- if ("web_search_request" in nextConfig) {
322
- delete nextConfig.web_search_request;
323
- }
311
+ removeDeprecatedKeys(nextConfig);
324
312
  const providerKey = resolveCodexProviderKey(provider);
325
313
  nextConfig.model_provider = providerKey;
326
- nextConfig.model = provider.model || nextConfig.model || "gpt-5.3-codex";
314
+ nextConfig.model = provider.model || nextConfig.model || "gpt-5.4";
327
315
  nextConfig.model_providers = {
328
- [providerKey]: {
329
- name: providerKey,
330
- base_url: provider.baseUrl,
331
- wire_api: "responses",
332
- requires_openai_auth: true
333
- }
316
+ [providerKey]: buildManagedProvider(provider, providerKey)
334
317
  };
335
318
  fs2.writeFileSync(configPath, stringifyToml(nextConfig), { mode: 384 });
336
319
  const authPath = getCodexAuthPath();
337
320
  const auth = { OPENAI_API_KEY: provider.apiKey };
338
321
  writeJSON(authPath, auth);
339
322
  }
340
- var __filename, __dirname, CODEX_DEFAULT_CONFIG;
323
+ function writeCodexConfigMerge(provider) {
324
+ ensureDir(getCodexDir());
325
+ const configPath = getCodexConfigPath();
326
+ const existingConfig = loadExistingCodexConfig(configPath);
327
+ const templateConfig = loadCodexTemplateConfig();
328
+ const nextConfig = deepMerge(templateConfig, existingConfig);
329
+ const providerKey = resolveCodexProviderKey(provider);
330
+ removeDeprecatedKeys(nextConfig);
331
+ nextConfig.model_provider = providerKey;
332
+ nextConfig.model = provider.model || nextConfig.model || "gpt-5.4";
333
+ const existingProviders = nextConfig.model_providers && typeof nextConfig.model_providers === "object" && !Array.isArray(nextConfig.model_providers) ? { ...nextConfig.model_providers } : {};
334
+ const lowerProviderKey = providerKey.toLowerCase();
335
+ for (const key of Object.keys(existingProviders)) {
336
+ if (key.toLowerCase() === lowerProviderKey) {
337
+ delete existingProviders[key];
338
+ }
339
+ }
340
+ nextConfig.model_providers = {
341
+ ...existingProviders,
342
+ [providerKey]: buildManagedProvider(provider, providerKey)
343
+ };
344
+ fs2.writeFileSync(configPath, stringifyToml(nextConfig), { mode: 384 });
345
+ const authPath = getCodexAuthPath();
346
+ let existingAuth = { OPENAI_API_KEY: "" };
347
+ if (fileExists(authPath)) {
348
+ try {
349
+ existingAuth = readJSON(authPath);
350
+ } catch {
351
+ existingAuth = { OPENAI_API_KEY: "" };
352
+ }
353
+ }
354
+ const nextAuth = {
355
+ ...existingAuth,
356
+ OPENAI_API_KEY: provider.apiKey
357
+ };
358
+ writeJSON(authPath, nextAuth);
359
+ }
360
+ function writeCodexConfig(provider, options = {}) {
361
+ if (options.mode === "overwrite") {
362
+ writeCodexConfigOverwrite(provider);
363
+ return;
364
+ }
365
+ writeCodexConfigMerge(provider);
366
+ }
367
+ var __filename, __dirname, CODEX_DEFAULT_CONFIG, GMN_PROVIDER_HOSTS;
341
368
  var init_codex = __esm({
342
369
  "../core/dist/writers/codex.js"() {
343
370
  "use strict";
344
371
  init_paths();
345
372
  init_file();
373
+ init_template();
346
374
  __filename = fileURLToPath(import.meta.url);
347
375
  __dirname = path3.dirname(__filename);
348
376
  CODEX_DEFAULT_CONFIG = {
349
- model: "gpt-5.3-codex",
377
+ model: "gpt-5.4",
350
378
  model_reasoning_effort: "xhigh",
351
379
  disable_response_storage: true,
352
380
  sandbox_mode: "danger-full-access",
@@ -392,35 +420,15 @@ var init_codex = __esm({
392
420
  hide_gpt5_1_migration_prompt: true
393
421
  }
394
422
  };
395
- }
396
- });
397
-
398
- // ../core/dist/utils/template.js
399
- function replaceVariables(template, variables) {
400
- const jsonStr = JSON.stringify(template);
401
- let result = jsonStr;
402
- for (const [key, value] of Object.entries(variables)) {
403
- const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
404
- result = result.replace(new RegExp(`{{${key}}}`, "g"), escapedValue);
405
- }
406
- return JSON.parse(result);
407
- }
408
- function deepMerge(target, source) {
409
- const result = { ...target };
410
- for (const key in source) {
411
- const sourceValue = source[key];
412
- const targetValue = result[key];
413
- if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
414
- result[key] = deepMerge(targetValue, sourceValue);
415
- } else {
416
- result[key] = sourceValue;
417
- }
418
- }
419
- return result;
420
- }
421
- var init_template = __esm({
422
- "../core/dist/utils/template.js"() {
423
- "use strict";
423
+ GMN_PROVIDER_HOSTS = [
424
+ "gmn.chuangzuoli.com",
425
+ "cdn.gmnchuangzuoli.com",
426
+ "gmncodex.com",
427
+ "gmncode.cn",
428
+ "cdn.gmncode.cn",
429
+ "gmn.codex.com",
430
+ "cdn.gmncode.com"
431
+ ];
424
432
  }
425
433
  });
426
434
 
@@ -454,19 +462,23 @@ function loadClaudeTemplateConfig() {
454
462
  }
455
463
  return CLAUDE_CONFIG_TEMPLATE;
456
464
  }
457
- function writeClaudeConfig(provider) {
465
+ function writeClaudeConfig(provider, options = {}) {
458
466
  ensureDir(getClaudeDir());
459
467
  const configPath = getClaudeConfigPath();
460
- let userConfig = {};
461
- if (fileExists(configPath)) {
462
- const content = fs3.readFileSync(configPath, "utf-8");
463
- userConfig = JSON.parse(content);
464
- }
465
468
  const defaultTemplate = loadClaudeTemplateConfig();
466
469
  const defaultConfig = replaceVariables(defaultTemplate, {
467
470
  apiKey: provider.apiKey,
468
471
  baseUrl: provider.baseUrl
469
472
  });
473
+ if (options.mode === "overwrite") {
474
+ fs3.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), { mode: 384 });
475
+ return;
476
+ }
477
+ let userConfig = {};
478
+ if (fileExists(configPath)) {
479
+ const content = fs3.readFileSync(configPath, "utf-8");
480
+ userConfig = JSON.parse(content);
481
+ }
470
482
  const mergedConfig = deepMerge(defaultConfig, userConfig);
471
483
  mergedConfig.env = mergedConfig.env || {};
472
484
  mergedConfig.env.ANTHROPIC_AUTH_TOKEN = provider.apiKey;
@@ -893,13 +905,14 @@ function saveEnvFile(envPath, env) {
893
905
  fs5.writeFileSync(tempPath, content, { mode: 384 });
894
906
  fs5.renameSync(tempPath, envPath);
895
907
  }
896
- function writeGeminiConfig(provider) {
908
+ function writeGeminiConfig(provider, options = {}) {
897
909
  const settingsPath = getGeminiSettingsPath();
898
910
  const envPath = getGeminiEnvPath();
899
911
  const dir = getGeminiDir();
900
912
  ensureDir(dir);
913
+ const settingsTemplate = loadGeminiSettingsTemplate();
901
914
  let userSettings = {};
902
- if (fileExists(settingsPath)) {
915
+ if (options.mode !== "overwrite" && fileExists(settingsPath)) {
903
916
  try {
904
917
  const content = fs5.readFileSync(settingsPath, "utf-8");
905
918
  const parsed = JSON.parse(content);
@@ -910,8 +923,7 @@ function writeGeminiConfig(provider) {
910
923
  throw new Error(`\u65E0\u6CD5\u8BFB\u53D6 Gemini settings.json: ${error.message}`);
911
924
  }
912
925
  }
913
- const settingsTemplate = loadGeminiSettingsTemplate();
914
- const settings = deepMerge(settingsTemplate, userSettings);
926
+ const settings = options.mode === "overwrite" ? { ...settingsTemplate } : deepMerge(settingsTemplate, userSettings);
915
927
  if (!settings.ide || typeof settings.ide !== "object") {
916
928
  settings.ide = {};
917
929
  }
@@ -936,7 +948,7 @@ function writeGeminiConfig(provider) {
936
948
  } catch (error) {
937
949
  throw new Error(`\u5199\u5165 Gemini settings.json \u5931\u8D25: ${error.message}`);
938
950
  }
939
- const existingEnv = loadEnvFile(envPath);
951
+ const existingEnv = options.mode === "overwrite" ? {} : loadEnvFile(envPath);
940
952
  const templateEnv = loadGeminiEnvTemplate(provider);
941
953
  const env = {
942
954
  ...existingEnv,
@@ -1063,7 +1075,8 @@ function enforceAgentStoreFalse(agent) {
1063
1075
  }
1064
1076
  function enforceModelStoreFalse(models) {
1065
1077
  const base = models && typeof models === "object" && !Array.isArray(models) ? models : {};
1066
- return deepMerge(base, {
1078
+ const mergedModels = deepMerge(DEFAULT_MODELS, base);
1079
+ return deepMerge(mergedModels, {
1067
1080
  [OPENCODE_MODEL_KEY]: {
1068
1081
  options: {
1069
1082
  store: false
@@ -1074,20 +1087,31 @@ function enforceModelStoreFalse(models) {
1074
1087
  high: {},
1075
1088
  xhigh: {}
1076
1089
  }
1090
+ },
1091
+ [OPENCODE_SECONDARY_MODEL_KEY]: {
1092
+ options: {
1093
+ store: false
1094
+ },
1095
+ variants: {
1096
+ low: {},
1097
+ medium: {},
1098
+ high: {},
1099
+ xhigh: {}
1100
+ }
1077
1101
  }
1078
1102
  });
1079
1103
  }
1080
- function writeOpenCodeConfig(provider) {
1104
+ function writeOpenCodeConfig(provider, options = {}) {
1081
1105
  ensureDir(getOpenCodeDir());
1082
1106
  const configPath = getOpenCodeConfigPath();
1083
- const existingConfig = fileExists(configPath) ? readJSON(configPath) : {};
1107
+ const existingConfig = options.mode === "overwrite" ? {} : fileExists(configPath) ? readJSON(configPath) : {};
1084
1108
  const meta = parseProviderMeta(provider.model);
1085
1109
  const template = loadOpenCodeTemplateConfig();
1086
1110
  const defaultConfig = replaceVariables(template, {
1087
1111
  baseUrl: provider.baseUrl,
1088
1112
  apiKey: provider.apiKey
1089
1113
  });
1090
- const mergedConfig = deepMerge(defaultConfig, existingConfig);
1114
+ const mergedConfig = options.mode === "overwrite" ? { ...defaultConfig } : deepMerge(defaultConfig, existingConfig);
1091
1115
  const templateProvider = defaultConfig.provider?.[OPENCODE_PROVIDER_KEY];
1092
1116
  const existingProvider = mergedConfig.provider?.[OPENCODE_PROVIDER_KEY];
1093
1117
  const models = enforceModelStoreFalse(meta?.models || existingProvider?.models || templateProvider?.models || DEFAULT_MODELS);
@@ -1100,7 +1124,7 @@ function writeOpenCodeConfig(provider) {
1100
1124
  },
1101
1125
  models
1102
1126
  });
1103
- const existingProviders = mergedConfig.provider && typeof mergedConfig.provider === "object" && !Array.isArray(mergedConfig.provider) ? { ...mergedConfig.provider } : {};
1127
+ const existingProviders = options.mode === "overwrite" ? {} : mergedConfig.provider && typeof mergedConfig.provider === "object" && !Array.isArray(mergedConfig.provider) ? { ...mergedConfig.provider } : {};
1104
1128
  const nextConfig = {
1105
1129
  ...mergedConfig,
1106
1130
  $schema: OPENCODE_SCHEMA,
@@ -1113,7 +1137,7 @@ function writeOpenCodeConfig(provider) {
1113
1137
  };
1114
1138
  writeJSON(configPath, nextConfig);
1115
1139
  }
1116
- var OPENCODE_SCHEMA, OPENCODE_PROVIDER_KEY, OPENCODE_MODEL, OPENCODE_MODEL_KEY, __filename4, __dirname4, DEFAULT_MODELS, OPENCODE_CONFIG_TEMPLATE;
1140
+ var OPENCODE_SCHEMA, OPENCODE_PROVIDER_KEY, OPENCODE_MODEL, OPENCODE_MODEL_KEY, OPENCODE_SECONDARY_MODEL_KEY, __filename4, __dirname4, DEFAULT_MODELS, OPENCODE_CONFIG_TEMPLATE;
1117
1141
  var init_opencode2 = __esm({
1118
1142
  "../core/dist/writers/opencode.js"() {
1119
1143
  "use strict";
@@ -1122,13 +1146,26 @@ var init_opencode2 = __esm({
1122
1146
  init_template();
1123
1147
  OPENCODE_SCHEMA = "https://opencode.ai/config.json";
1124
1148
  OPENCODE_PROVIDER_KEY = "openai";
1125
- OPENCODE_MODEL = "openai/gpt-5.2-codex";
1126
- OPENCODE_MODEL_KEY = "gpt-5.2-codex";
1149
+ OPENCODE_MODEL = "openai/gpt-5.4";
1150
+ OPENCODE_MODEL_KEY = "gpt-5.4";
1151
+ OPENCODE_SECONDARY_MODEL_KEY = "gpt-5.3-codex";
1127
1152
  __filename4 = fileURLToPath4(import.meta.url);
1128
1153
  __dirname4 = path7.dirname(__filename4);
1129
1154
  DEFAULT_MODELS = {
1130
1155
  [OPENCODE_MODEL_KEY]: {
1131
- name: "GPT-5.2 Codex",
1156
+ name: "GPT-5.4",
1157
+ options: {
1158
+ store: false
1159
+ },
1160
+ variants: {
1161
+ low: {},
1162
+ medium: {},
1163
+ high: {},
1164
+ xhigh: {}
1165
+ }
1166
+ },
1167
+ [OPENCODE_SECONDARY_MODEL_KEY]: {
1168
+ name: "GPT-5.3 Codex",
1132
1169
  options: {
1133
1170
  store: false
1134
1171
  },
@@ -1257,7 +1294,7 @@ function replaceProviderEntry(providers, providerName, nextProvider) {
1257
1294
  }
1258
1295
  return result;
1259
1296
  }
1260
- function writeOpenClawConfig(provider) {
1297
+ function writeOpenClawConfig(provider, options = {}) {
1261
1298
  const configPath = getOpenClawConfigPath();
1262
1299
  const modelsPath = getOpenClawModelsPath();
1263
1300
  const homeDir = path8.dirname(getOpenClawDir());
@@ -1273,6 +1310,26 @@ function writeOpenClawConfig(provider) {
1273
1310
  };
1274
1311
  const nextOpenClawConfig = replaceVariables(rawConfigTemplate, variables);
1275
1312
  const nextModelsConfig = replaceVariables(rawModelsTemplate, variables);
1313
+ if (options.mode === "overwrite") {
1314
+ const overwriteConfig = {
1315
+ ...nextOpenClawConfig,
1316
+ agents: {
1317
+ ...nextOpenClawConfig.agents,
1318
+ defaults: {
1319
+ ...nextOpenClawConfig.agents?.defaults || {},
1320
+ workspace: homeDir,
1321
+ imageModel: nextOpenClawConfig.agents?.defaults?.imageModel || `${providerName}/gpt-5.4`,
1322
+ model: {
1323
+ ...nextOpenClawConfig.agents?.defaults?.model || {},
1324
+ primary: nextOpenClawConfig.agents?.defaults?.model?.primary || `${providerName}/gpt-5.4`
1325
+ }
1326
+ }
1327
+ }
1328
+ };
1329
+ writeJSON(configPath, overwriteConfig);
1330
+ writeJSON(modelsPath, nextModelsConfig);
1331
+ return;
1332
+ }
1276
1333
  const existingOpenClawConfig = loadExistingJSON(configPath) || {};
1277
1334
  const existingModelsConfig = loadExistingJSON(modelsPath) || {};
1278
1335
  const mergedConfigModels = deepMerge(existingOpenClawConfig.models || {}, nextOpenClawConfig.models || {});
@@ -1282,7 +1339,8 @@ function writeOpenClawConfig(provider) {
1282
1339
  const mergedAgents = deepMerge(nextOpenClawConfig.agents || {}, existingOpenClawConfig.agents || {});
1283
1340
  const mergedDefaults = mergedAgents.defaults || {};
1284
1341
  const mergedModel = mergedDefaults.model || {};
1285
- const templatePrimary = nextOpenClawConfig.agents?.defaults?.model?.primary || `${providerName}/gpt-5.3-codex`;
1342
+ const templatePrimary = nextOpenClawConfig.agents?.defaults?.model?.primary || `${providerName}/gpt-5.4`;
1343
+ const templateImageModel = nextOpenClawConfig.agents?.defaults?.imageModel || `${providerName}/gpt-5.4`;
1286
1344
  const workspace = typeof mergedDefaults.workspace === "string" && mergedDefaults.workspace.trim() ? mergedDefaults.workspace : homeDir;
1287
1345
  const finalOpenClawConfig = {
1288
1346
  ...existingOpenClawConfig,
@@ -1292,6 +1350,7 @@ function writeOpenClawConfig(provider) {
1292
1350
  defaults: {
1293
1351
  ...mergedDefaults,
1294
1352
  workspace,
1353
+ imageModel: templateImageModel,
1295
1354
  model: {
1296
1355
  ...mergedModel,
1297
1356
  primary: templatePrimary
@@ -1308,7 +1367,7 @@ function writeOpenClawConfig(provider) {
1308
1367
  writeJSON(configPath, finalOpenClawConfig);
1309
1368
  writeJSON(modelsPath, finalModelsConfig);
1310
1369
  }
1311
- var DEFAULT_PROVIDER_NAME, PRIMARY_MODEL_ID, __filename5, __dirname5, OPENCLAW_CONFIG_TEMPLATE, OPENCLAW_MODELS_TEMPLATE;
1370
+ var DEFAULT_PROVIDER_NAME, PRIMARY_MODEL_ID, SECONDARY_MODEL_ID, PRIMARY_MODEL_INPUTS, SECONDARY_MODEL_INPUTS, __filename5, __dirname5, OPENCLAW_CONFIG_TEMPLATE, OPENCLAW_MODELS_TEMPLATE;
1312
1371
  var init_openclaw2 = __esm({
1313
1372
  "../core/dist/writers/openclaw.js"() {
1314
1373
  "use strict";
@@ -1316,7 +1375,10 @@ var init_openclaw2 = __esm({
1316
1375
  init_file();
1317
1376
  init_template();
1318
1377
  DEFAULT_PROVIDER_NAME = "gmn";
1319
- PRIMARY_MODEL_ID = "gpt-5.3-codex";
1378
+ PRIMARY_MODEL_ID = "gpt-5.4";
1379
+ SECONDARY_MODEL_ID = "gpt-5.3-codex";
1380
+ PRIMARY_MODEL_INPUTS = ["text", "image"];
1381
+ SECONDARY_MODEL_INPUTS = ["text", "image"];
1320
1382
  __filename5 = fileURLToPath5(import.meta.url);
1321
1383
  __dirname5 = path8.dirname(__filename5);
1322
1384
  OPENCLAW_CONFIG_TEMPLATE = {
@@ -1334,24 +1396,24 @@ var init_openclaw2 = __esm({
1334
1396
  authHeader: true,
1335
1397
  models: [
1336
1398
  {
1337
- id: "gpt-5.3-codex",
1338
- name: "gpt-5.3-codex",
1399
+ id: "gpt-5.4",
1400
+ name: "gpt-5.4",
1339
1401
  api: "openai-responses",
1340
- reasoning: false,
1341
- input: ["text"],
1402
+ reasoning: true,
1403
+ input: PRIMARY_MODEL_INPUTS,
1342
1404
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1343
- contextWindow: 2e5,
1344
- maxTokens: 8192
1405
+ contextWindow: 105e4,
1406
+ maxTokens: 128e3
1345
1407
  },
1346
1408
  {
1347
- id: "gpt-5.2-codex",
1348
- name: "gpt-5.2-codex",
1409
+ id: SECONDARY_MODEL_ID,
1410
+ name: SECONDARY_MODEL_ID,
1349
1411
  api: "openai-responses",
1350
1412
  reasoning: false,
1351
- input: ["text"],
1413
+ input: SECONDARY_MODEL_INPUTS,
1352
1414
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1353
- contextWindow: 2e5,
1354
- maxTokens: 8192
1415
+ contextWindow: 4e5,
1416
+ maxTokens: 128e3
1355
1417
  }
1356
1418
  ]
1357
1419
  }
@@ -1360,8 +1422,9 @@ var init_openclaw2 = __esm({
1360
1422
  agents: {
1361
1423
  defaults: {
1362
1424
  workspace: "",
1425
+ imageModel: "{{providerName}}/gpt-5.4",
1363
1426
  model: {
1364
- primary: "{{providerName}}/gpt-5.3-codex"
1427
+ primary: "{{providerName}}/gpt-5.4"
1365
1428
  },
1366
1429
  thinkingDefault: "xhigh"
1367
1430
  }
@@ -1380,24 +1443,24 @@ var init_openclaw2 = __esm({
1380
1443
  },
1381
1444
  models: [
1382
1445
  {
1383
- id: "gpt-5.3-codex",
1384
- name: "gpt-5.3-codex",
1446
+ id: "gpt-5.4",
1447
+ name: "gpt-5.4",
1385
1448
  api: "openai-responses",
1386
- reasoning: false,
1387
- input: ["text"],
1449
+ reasoning: true,
1450
+ input: PRIMARY_MODEL_INPUTS,
1388
1451
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1389
- contextWindow: 2e5,
1390
- maxTokens: 8192
1452
+ contextWindow: 105e4,
1453
+ maxTokens: 128e3
1391
1454
  },
1392
1455
  {
1393
- id: "gpt-5.2-codex",
1394
- name: "gpt-5.2-codex",
1456
+ id: SECONDARY_MODEL_ID,
1457
+ name: SECONDARY_MODEL_ID,
1395
1458
  api: "openai-responses",
1396
1459
  reasoning: false,
1397
- input: ["text"],
1460
+ input: SECONDARY_MODEL_INPUTS,
1398
1461
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
1399
- contextWindow: 2e5,
1400
- maxTokens: 8192
1462
+ contextWindow: 4e5,
1463
+ maxTokens: 128e3
1401
1464
  }
1402
1465
  ]
1403
1466
  }
@@ -1537,7 +1600,7 @@ function createToolManager(tool) {
1537
1600
  return void 0;
1538
1601
  return config.providers.find((p) => p.name.trim().toLowerCase() === lowerName);
1539
1602
  },
1540
- switch(id) {
1603
+ switch(id, options) {
1541
1604
  const config = loadConfig2();
1542
1605
  const provider = config.providers.find((p) => p.id === id);
1543
1606
  if (!provider) {
@@ -1546,7 +1609,7 @@ function createToolManager(tool) {
1546
1609
  config.currentProviderId = id;
1547
1610
  provider.lastUsedAt = Date.now();
1548
1611
  saveConfig2(config);
1549
- toolConfig.writer(provider);
1612
+ toolConfig.writer(provider, options);
1550
1613
  },
1551
1614
  getCurrent() {
1552
1615
  const config = loadConfig2();
@@ -1559,10 +1622,11 @@ function createToolManager(tool) {
1559
1622
  // 注:edit 方法的"复杂度"来自必要的业务逻辑(检查存在性、名称冲突、更新 4 个可选字段、同步配置)
1560
1623
  // 每个 if 都不可避免,没有特殊情况或嵌套逻辑,因此禁用 complexity 检查
1561
1624
  // eslint-disable-next-line complexity
1562
- edit(id, updates) {
1625
+ edit(id, updates, options = {}) {
1563
1626
  const config = loadConfig2();
1564
1627
  const provider = config.providers.find((p) => p.id === id);
1565
1628
  const normalizedUpdates = trimProviderUpdates(updates);
1629
+ const shouldApplyWrite = options.applyWrite !== false;
1566
1630
  if (!provider) {
1567
1631
  throw new ProviderNotFoundError(id);
1568
1632
  }
@@ -1587,10 +1651,10 @@ function createToolManager(tool) {
1587
1651
  provider.model = normalizedUpdates.model;
1588
1652
  provider.lastModified = Date.now();
1589
1653
  saveConfig2(config);
1590
- if (config.currentProviderId === id) {
1654
+ if (shouldApplyWrite && config.currentProviderId === id) {
1591
1655
  toolConfig.writer(provider);
1592
1656
  }
1593
- if (toolConfig.autoSync) {
1657
+ if (shouldApplyWrite && toolConfig.autoSync) {
1594
1658
  toolConfig.writer(provider);
1595
1659
  }
1596
1660
  return provider;
@@ -2806,11 +2870,11 @@ var init_claude_clean = __esm({
2806
2870
  });
2807
2871
 
2808
2872
  // ../core/dist/index.js
2809
- var VERSION;
2873
+ import { readFileSync as readFileSync10 } from "fs";
2874
+ var pkg, VERSION;
2810
2875
  var init_dist2 = __esm({
2811
2876
  "../core/dist/index.js"() {
2812
2877
  "use strict";
2813
- init_package();
2814
2878
  init_constants();
2815
2879
  init_tool_manager();
2816
2880
  init_codex2();
@@ -2829,7 +2893,8 @@ var init_dist2 = __esm({
2829
2893
  init_merge_advanced();
2830
2894
  init_export();
2831
2895
  init_claude_clean();
2832
- VERSION = package_default.version;
2896
+ pkg = JSON.parse(readFileSync10(new URL("../package.json", import.meta.url), "utf-8"));
2897
+ VERSION = pkg.version;
2833
2898
  }
2834
2899
  });
2835
2900
 
@@ -7151,20 +7216,187 @@ import fs13 from "fs";
7151
7216
  import path15 from "path";
7152
7217
  import chalk54 from "chalk";
7153
7218
  import inquirer38 from "inquirer";
7154
- var DEFAULT_PROVIDER_NAME2 = "gmn";
7155
- var VALID_PLATFORMS = ["codex", "opencode", "openclaw"];
7156
- var DEFAULT_PLATFORMS = ["codex", "opencode"];
7219
+
7220
+ // src/utils/endpoint-latency.ts
7221
+ import { request as httpRequest } from "http";
7222
+ import { request as httpsRequest } from "https";
7223
+ import { performance } from "perf_hooks";
7224
+ function normalizeEndpointUrl(url) {
7225
+ const normalized = url.trim().replace(/\/+$/, "");
7226
+ new URL(normalized);
7227
+ return normalized;
7228
+ }
7229
+ function calculateMedian(values) {
7230
+ if (values.length === 0) {
7231
+ throw new Error("\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u91C7\u6837\u503C");
7232
+ }
7233
+ const sorted = [...values].sort((a, b) => a - b);
7234
+ const middle = Math.floor(sorted.length / 2);
7235
+ if (sorted.length % 2 === 0) {
7236
+ return Math.round((sorted[middle - 1] + sorted[middle]) / 2);
7237
+ }
7238
+ return sorted[middle];
7239
+ }
7240
+ function probeOnce(url, timeoutMs) {
7241
+ return new Promise((resolve6) => {
7242
+ const target = new URL(url);
7243
+ const requester = target.protocol === "http:" ? httpRequest : httpsRequest;
7244
+ const start = performance.now();
7245
+ let settled = false;
7246
+ const finish = (result) => {
7247
+ if (settled) return;
7248
+ settled = true;
7249
+ resolve6(result);
7250
+ };
7251
+ const req = requester(
7252
+ target,
7253
+ {
7254
+ method: "GET",
7255
+ headers: {
7256
+ Accept: "application/json",
7257
+ "Cache-Control": "no-cache",
7258
+ "User-Agent": "ccman-latency-probe/1.0"
7259
+ }
7260
+ },
7261
+ (response) => {
7262
+ const latencyMs = Math.max(1, Math.round(performance.now() - start));
7263
+ const statusCode = response.statusCode ?? null;
7264
+ response.destroy();
7265
+ finish({ latencyMs, statusCode });
7266
+ }
7267
+ );
7268
+ req.setTimeout(timeoutMs, () => {
7269
+ req.destroy(new Error(`\u6D4B\u901F\u8D85\u65F6\uFF08>${timeoutMs}ms\uFF09`));
7270
+ });
7271
+ req.on("error", (error) => {
7272
+ finish({
7273
+ latencyMs: null,
7274
+ statusCode: null,
7275
+ error: error.message
7276
+ });
7277
+ });
7278
+ req.end();
7279
+ });
7280
+ }
7281
+ async function probeCandidate(candidate, originalIndex, options) {
7282
+ const normalizedUrl = normalizeEndpointUrl(candidate.url);
7283
+ const normalizedProbeUrl = candidate.probeUrl ? normalizeEndpointUrl(candidate.probeUrl) : normalizedUrl;
7284
+ const samples = [];
7285
+ let statusCode = null;
7286
+ let lastError;
7287
+ for (let index = 0; index < options.sampleCount; index += 1) {
7288
+ const result = await probeOnce(normalizedProbeUrl, options.timeoutMs);
7289
+ if (result.latencyMs !== null) {
7290
+ samples.push(result.latencyMs);
7291
+ statusCode = result.statusCode;
7292
+ } else if (!lastError) {
7293
+ lastError = result.error;
7294
+ }
7295
+ }
7296
+ return {
7297
+ ...candidate,
7298
+ url: normalizedUrl,
7299
+ probeUrl: normalizedProbeUrl,
7300
+ originalIndex,
7301
+ samples,
7302
+ statusCode,
7303
+ latencyMs: samples.length > 0 ? calculateMedian(samples) : null,
7304
+ error: samples.length > 0 ? void 0 : lastError || "\u6D4B\u901F\u5931\u8D25"
7305
+ };
7306
+ }
7307
+ async function probeEndpointCandidates(candidates, options = {}) {
7308
+ const resolvedOptions = {
7309
+ sampleCount: options.sampleCount ?? 3,
7310
+ timeoutMs: options.timeoutMs ?? 2500
7311
+ };
7312
+ return Promise.all(
7313
+ candidates.map((candidate, index) => probeCandidate(candidate, index, resolvedOptions))
7314
+ );
7315
+ }
7316
+ function sortEndpointProbeResults(results) {
7317
+ return [...results].sort((left, right) => {
7318
+ const leftReachable = left.latencyMs !== null;
7319
+ const rightReachable = right.latencyMs !== null;
7320
+ if (leftReachable && !rightReachable) return -1;
7321
+ if (!leftReachable && rightReachable) return 1;
7322
+ if (!leftReachable && !rightReachable) return left.originalIndex - right.originalIndex;
7323
+ if (left.latencyMs !== right.latencyMs) {
7324
+ return (left.latencyMs || 0) - (right.latencyMs || 0);
7325
+ }
7326
+ return left.originalIndex - right.originalIndex;
7327
+ });
7328
+ }
7329
+ function pickDefaultEndpoint(results) {
7330
+ return sortEndpointProbeResults(results)[0];
7331
+ }
7332
+
7333
+ // src/utils/gmn-endpoints.ts
7157
7334
  var GMN_PROFILE = {
7158
7335
  commandName: "gmn",
7159
7336
  title: "GMN",
7160
- openaiBaseUrl: "https://gmn.chuangzuoli.com"
7337
+ endpointGroupLabel: "\u521B\u4F5C\u91CC\u57DF\u540D\u7EBF\u8DEF",
7338
+ endpointGroupDescription: "\u4EC5\u5305\u542B chuangzuoli \u57DF\u540D\u4E0B\u7684 2 \u6761\u5730\u5740",
7339
+ baseUrls: [
7340
+ {
7341
+ label: "\u521B\u4F5C\u91CC\u4E3B\u7AD9",
7342
+ url: "https://gmn.chuangzuoli.com",
7343
+ description: "chuangzuoli \u4E3B\u57DF\u540D\u5165\u53E3"
7344
+ },
7345
+ {
7346
+ label: "\u521B\u4F5C\u91CC CDN",
7347
+ url: "https://cdn.gmnchuangzuoli.com",
7348
+ description: "chuangzuoli \u57DF\u540D CDN \u52A0\u901F\u5165\u53E3"
7349
+ }
7350
+ ]
7161
7351
  };
7162
7352
  var GMN1_PROFILE = {
7163
7353
  commandName: "gmn1",
7164
7354
  title: "GMN1",
7165
- openaiBaseUrl: "https://gmncode.cn"
7355
+ endpointGroupLabel: "\u6269\u5C55\u52A0\u901F\u7EBF\u8DEF",
7356
+ endpointGroupDescription: "\u5305\u542B\u9664 chuangzuoli \u4E4B\u5916\u7684\u5176\u4F59 5 \u6761\u5730\u5740",
7357
+ baseUrls: [
7358
+ {
7359
+ label: "\u963F\u91CC\u4E91 CDN",
7360
+ url: "https://gmncodex.com",
7361
+ description: "\u963F\u91CC\u4E91\u89E3\u6790 CDN \u56DE\u56FD\u52A0\u901F"
7362
+ },
7363
+ {
7364
+ label: "\u5168\u7403\u8FB9\u7F18 A",
7365
+ url: "https://gmncode.cn",
7366
+ description: "\u5168\u7403\u8FB9\u7F18\u8282\u70B9\u52A0\u901F"
7367
+ },
7368
+ {
7369
+ label: "CF CDN A",
7370
+ url: "https://cdn.gmncode.cn",
7371
+ description: "CF \u89E3\u6790 CDN \u56DE\u56FD\u52A0\u901F"
7372
+ },
7373
+ {
7374
+ label: "\u5168\u7403\u8FB9\u7F18 B",
7375
+ url: "https://gmn.codex.com",
7376
+ description: "\u5168\u7403\u8FB9\u7F18\u8282\u70B9\u52A0\u901F"
7377
+ },
7378
+ {
7379
+ label: "CF CDN B",
7380
+ url: "https://cdn.gmncode.com",
7381
+ description: "CF \u89E3\u6790 CDN \u56DE\u56FD\u52A0\u901F"
7382
+ }
7383
+ ]
7166
7384
  };
7167
- var TOTAL_STEPS = 3;
7385
+ function getEndpointHost(url) {
7386
+ return new URL(url).host;
7387
+ }
7388
+ function formatEndpointChoiceLabel(result, index) {
7389
+ const latencyText = result.latencyMs === null ? result.error || "\u6D4B\u901F\u5931\u8D25" : `${result.latencyMs} ms`;
7390
+ return `${index + 1}. ${result.label} | ${getEndpointHost(result.url)} | ${latencyText}`;
7391
+ }
7392
+
7393
+ // src/commands/gmn.ts
7394
+ var DEFAULT_PROVIDER_NAME2 = "gmn";
7395
+ var VALID_PLATFORMS = ["codex", "opencode", "openclaw"];
7396
+ var DEFAULT_PLATFORMS = ["codex", "opencode"];
7397
+ var TOTAL_STEPS = 4;
7398
+ var BASE_URL_PROBE_SAMPLE_COUNT = 3;
7399
+ var BASE_URL_PROBE_TIMEOUT_MS = 2500;
7168
7400
  function renderStep(current, total, title) {
7169
7401
  const barLength = total;
7170
7402
  const filledLength = Math.min(current, total);
@@ -7280,6 +7512,84 @@ async function resolvePlatforms(platformArg, title = "GMN") {
7280
7512
  }
7281
7513
  return promptPlatforms(title);
7282
7514
  }
7515
+ function formatLatency(result) {
7516
+ if (result.latencyMs === null) {
7517
+ return result.error || "\u6D4B\u901F\u5931\u8D25";
7518
+ }
7519
+ return `${result.latencyMs} ms`;
7520
+ }
7521
+ function buildProbeCandidates(baseUrls, platforms) {
7522
+ const openClawOnly = platforms.length === 1 && platforms[0] === "openclaw";
7523
+ return baseUrls.map((item) => ({
7524
+ ...item,
7525
+ probeUrl: openClawOnly ? buildOpenClawBaseUrl(item.url) : item.url
7526
+ }));
7527
+ }
7528
+ function printBaseUrlProbeResults(profile, results, platforms) {
7529
+ const usingOpenClawPath = platforms.length === 1 && platforms[0] === "openclaw";
7530
+ console.log(chalk54.bold(`\u5F53\u524D\u6D4B\u901F\u8303\u56F4\uFF1A${profile.endpointGroupLabel}`));
7531
+ console.log(chalk54.gray(`${profile.endpointGroupDescription}\uFF08\u5171 ${results.length} \u6761\uFF09`));
7532
+ console.log(
7533
+ chalk54.gray(
7534
+ `\u6D4B\u901F\u65B9\u5F0F\uFF1AHTTPS \u9996\u5305\u5EF6\u8FDF\uFF08${BASE_URL_PROBE_SAMPLE_COUNT} \u6B21\u4E2D\u4F4D\u6570\uFF09${usingOpenClawPath ? "\uFF0C\u5F53\u524D\u4EC5\u68C0\u6D4B OpenClaw \u7684 /v1 \u7AEF\u70B9" : ""}`
7535
+ )
7536
+ );
7537
+ console.log();
7538
+ for (const [index, result] of results.entries()) {
7539
+ const latencyText = result.latencyMs === null ? chalk54.red(formatLatency(result)) : chalk54.green(formatLatency(result));
7540
+ console.log(` ${chalk54.cyan(`${index + 1}. ${result.label}`)}`);
7541
+ console.log(chalk54.gray(` \u57DF\u540D: ${getEndpointHost(result.url)}`));
7542
+ console.log(chalk54.gray(` \u5730\u5740: ${result.url}`));
7543
+ console.log(chalk54.gray(` \u8BF4\u660E: ${result.description}`));
7544
+ console.log(` \u5EF6\u8FDF: ${latencyText}`);
7545
+ console.log();
7546
+ }
7547
+ }
7548
+ async function resolveOpenAiBaseUrl(profile, platforms, baseUrlArg) {
7549
+ if (baseUrlArg && baseUrlArg.trim().length > 0) {
7550
+ const normalized = normalizeEndpointUrl(baseUrlArg);
7551
+ console.log(chalk54.gray(`\u5DF2\u901A\u8FC7\u53C2\u6570\u6307\u5B9A Base URL: ${normalized}`));
7552
+ return normalized;
7553
+ }
7554
+ const probeResults = sortEndpointProbeResults(
7555
+ await probeEndpointCandidates(buildProbeCandidates(profile.baseUrls, platforms), {
7556
+ sampleCount: BASE_URL_PROBE_SAMPLE_COUNT,
7557
+ timeoutMs: BASE_URL_PROBE_TIMEOUT_MS
7558
+ })
7559
+ );
7560
+ if (probeResults.length === 0) {
7561
+ throw new Error("\u6CA1\u6709\u53EF\u7528\u7684 Base URL \u5019\u9009\u9879");
7562
+ }
7563
+ printBaseUrlProbeResults(profile, probeResults, platforms);
7564
+ const defaultResult = pickDefaultEndpoint(probeResults) || probeResults[0];
7565
+ const allFailed = probeResults.every((result) => result.latencyMs === null);
7566
+ if (allFailed) {
7567
+ console.log(chalk54.yellow("\u26A0\uFE0F \u6240\u6709\u5019\u9009\u5730\u5740\u6D4B\u901F\u5931\u8D25\uFF0C\u5C06\u9ED8\u8BA4\u9009\u4E2D\u7B2C\u4E00\u4E2A\u5730\u5740\uFF0C\u4F60\u4E5F\u53EF\u4EE5\u624B\u52A8\u5207\u6362\u3002"));
7568
+ } else {
7569
+ console.log(chalk54.gray(`\u9ED8\u8BA4\u5DF2\u9009\u6700\u4F4E\u5EF6\u8FDF\u7EBF\u8DEF\uFF1A${defaultResult.url}`));
7570
+ }
7571
+ if (probeResults.length === 1) {
7572
+ console.log(chalk54.green(`\u5DF2\u81EA\u52A8\u9009\u62E9: ${defaultResult.url}`));
7573
+ return defaultResult.url;
7574
+ }
7575
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
7576
+ console.log(chalk54.yellow(`\u975E\u4EA4\u4E92\u73AF\u5883\uFF0C\u5DF2\u81EA\u52A8\u4F7F\u7528\u9ED8\u8BA4\u5730\u5740\uFF1A${defaultResult.url}`));
7577
+ return defaultResult.url;
7578
+ }
7579
+ const answers = await inquirer38.prompt([
7580
+ {
7581
+ type: "list",
7582
+ name: "baseUrl",
7583
+ message: "\u9009\u62E9\u8981\u4F7F\u7528\u7684 Base URL\uFF08\u9ED8\u8BA4\u5DF2\u9009\u6700\u4F4E\u5EF6\u8FDF\uFF0C\u53EF\u53C2\u8003\u4E0A\u65B9\u7F16\u53F7\uFF09:",
7584
+ choices: probeResults.map((result, index) => ({
7585
+ name: formatEndpointChoiceLabel(result, index),
7586
+ value: result.url
7587
+ })),
7588
+ default: defaultResult.url
7589
+ }
7590
+ ]);
7591
+ return answers.baseUrl;
7592
+ }
7283
7593
  function resolveProviderName2(providerNameArg) {
7284
7594
  if (providerNameArg === void 0) {
7285
7595
  return DEFAULT_PROVIDER_NAME2;
@@ -7369,7 +7679,7 @@ function buildOpenClawBaseUrl(openaiBaseUrl) {
7369
7679
  const normalized = openaiBaseUrl.replace(/\/+$/, "");
7370
7680
  return `${normalized}/v1`;
7371
7681
  }
7372
- async function runGmnCommand(profile, apiKey, platformArg, providerNameArg) {
7682
+ async function runGmnCommand(profile, apiKey, platformArg, providerNameArg, baseUrlArg) {
7373
7683
  printBanner(profile.title);
7374
7684
  let platforms;
7375
7685
  let providerName;
@@ -7385,9 +7695,24 @@ ${renderStep(1, TOTAL_STEPS, "\u9009\u62E9\u8981\u914D\u7F6E\u7684\u5DE5\u5177")
7385
7695
  console.log(chalk54.gray(`\u5DF2\u9009\u62E9: ${platforms.join(", ")}`));
7386
7696
  console.log(chalk54.gray(`\u670D\u52A1\u5546\u540D\u79F0: ${providerName}`));
7387
7697
  printKeyNotice();
7698
+ console.log(chalk54.cyan(`
7699
+ ${renderStep(2, TOTAL_STEPS, "\u6D4B\u901F\u5E76\u9009\u62E9 Base URL")}`));
7700
+ let openaiBaseUrl;
7701
+ try {
7702
+ openaiBaseUrl = await resolveOpenAiBaseUrl(profile, platforms, baseUrlArg);
7703
+ } catch (error) {
7704
+ console.error(chalk54.red(`\u274C ${error.message}`));
7705
+ process.exit(1);
7706
+ }
7707
+ const openclawBaseUrl = buildOpenClawBaseUrl(openaiBaseUrl);
7708
+ const platformBaseUrls = {
7709
+ codex: openaiBaseUrl,
7710
+ opencode: openaiBaseUrl,
7711
+ openclaw: openclawBaseUrl
7712
+ };
7388
7713
  let resolvedApiKey = apiKey?.trim();
7389
7714
  console.log(chalk54.cyan(`
7390
- ${renderStep(2, TOTAL_STEPS, "\u8F93\u5165 API Key")}`));
7715
+ ${renderStep(3, TOTAL_STEPS, "\u8F93\u5165 API Key")}`));
7391
7716
  if (!resolvedApiKey) {
7392
7717
  resolvedApiKey = await promptApiKey(profile.title);
7393
7718
  } else {
@@ -7397,19 +7722,10 @@ ${renderStep(2, TOTAL_STEPS, "\u8F93\u5165 API Key")}`));
7397
7722
  console.error(chalk54.red("\u274C \u9519\u8BEF: API Key \u4E0D\u80FD\u4E3A\u7A7A"));
7398
7723
  process.exit(1);
7399
7724
  }
7400
- const openaiBaseUrl = profile.openaiBaseUrl;
7401
- const openclawBaseUrl = buildOpenClawBaseUrl(openaiBaseUrl);
7402
- const platformBaseUrls = {
7403
- codex: openaiBaseUrl,
7404
- opencode: openaiBaseUrl,
7405
- openclaw: openclawBaseUrl
7406
- };
7407
7725
  console.log(chalk54.cyan(`
7408
- ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7726
+ ${renderStep(4, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7409
7727
  console.log(chalk54.gray(`\u5DF2\u9009\u62E9\u5E73\u53F0: ${platforms.join(", ")}`));
7410
- if (platforms.includes("codex") || platforms.includes("opencode")) {
7411
- console.log(chalk54.gray(`OpenAI Base URL: ${openaiBaseUrl}`));
7412
- }
7728
+ console.log(chalk54.gray(`OpenAI Base URL: ${openaiBaseUrl}`));
7413
7729
  if (platforms.includes("openclaw")) {
7414
7730
  console.log(chalk54.gray(`OpenClaw Base URL: ${openclawBaseUrl}`));
7415
7731
  }
@@ -7447,8 +7763,12 @@ ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7447
7763
  }
7448
7764
  const baseUrl = platformBaseUrls[platform];
7449
7765
  const existing = findPreferredProvider(manager.list(), providerName);
7450
- const provider = existing ? manager.edit(existing.id, { name: providerName, baseUrl, apiKey: resolvedApiKey }) : manager.add({ name: providerName, baseUrl, apiKey: resolvedApiKey });
7451
- manager.switch(provider.id);
7766
+ const provider = existing ? manager.edit(
7767
+ existing.id,
7768
+ { name: providerName, baseUrl, apiKey: resolvedApiKey },
7769
+ { applyWrite: false }
7770
+ ) : manager.add({ name: providerName, baseUrl, apiKey: resolvedApiKey });
7771
+ manager.switch(provider.id, { mode: "overwrite" });
7452
7772
  completed += 1;
7453
7773
  console.log(chalk54.green(`\u2705 ${name}`));
7454
7774
  successBackups.push({
@@ -7494,11 +7814,11 @@ ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7494
7814
  }
7495
7815
  console.log(chalk54.gray("\u63D0\u793A\uFF1A\u8BF7\u91CD\u542F\u5BF9\u5E94\u5DE5\u5177/\u63D2\u4EF6\u4EE5\u4F7F\u914D\u7F6E\u751F\u6548\u3002"));
7496
7816
  }
7497
- async function gmnCommand(apiKey, platformArg, providerNameArg) {
7498
- await runGmnCommand(GMN_PROFILE, apiKey, platformArg, providerNameArg);
7817
+ async function gmnCommand(apiKey, platformArg, providerNameArg, baseUrlArg) {
7818
+ await runGmnCommand(GMN_PROFILE, apiKey, platformArg, providerNameArg, baseUrlArg);
7499
7819
  }
7500
- async function gmn1Command(apiKey, platformArg, providerNameArg) {
7501
- await runGmnCommand(GMN1_PROFILE, apiKey, platformArg, providerNameArg);
7820
+ async function gmn1Command(apiKey, platformArg, providerNameArg, baseUrlArg) {
7821
+ await runGmnCommand(GMN1_PROFILE, apiKey, platformArg, providerNameArg, baseUrlArg);
7502
7822
  }
7503
7823
 
7504
7824
  // src/index.ts
@@ -7536,7 +7856,8 @@ program.on("command:*", (operands) => {
7536
7856
  "export",
7537
7857
  "import",
7538
7858
  "gmn",
7539
- "gmn1"
7859
+ "gmn1",
7860
+ "gmncode"
7540
7861
  ];
7541
7862
  const suggestions = availableCommands.filter(
7542
7863
  (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
@@ -7595,11 +7916,11 @@ sync.action(async () => {
7595
7916
  });
7596
7917
  exportCommand(program);
7597
7918
  importCommand(program);
7598
- program.command("gmn [apiKey]").description("\u914D\u7F6E GMN \u5230 Codex\u3001OpenCode\u3001OpenClaw").option("-p, --platform <platforms>", "\u6307\u5B9A\u5E73\u53F0 (codex,opencode,openclaw,all)").option("-n, --name <providerName>", "\u6307\u5B9A\u670D\u52A1\u5546\u540D\u79F0\uFF08\u9ED8\u8BA4: gmn\uFF09").action(async (apiKey, options) => {
7599
- await gmnCommand(apiKey, options.platform, options.name);
7919
+ program.command("gmn [apiKey]").description("\u914D\u7F6E GMN \u5230 Codex\u3001OpenCode\u3001OpenClaw\uFF08\u6D4B\u901F\u5E76\u9009\u62E9\u6700\u4F4E\u5EF6\u8FDF\u8DEF\u7EBF\uFF09").option("-p, --platform <platforms>", "\u6307\u5B9A\u5E73\u53F0 (codex,opencode,openclaw,all)").option("-n, --name <providerName>", "\u6307\u5B9A\u670D\u52A1\u5546\u540D\u79F0\uFF08\u9ED8\u8BA4: gmn\uFF09").option("-b, --base-url <baseUrl>", "\u6307\u5B9A Base URL\uFF1B\u4E0D\u6307\u5B9A\u65F6\u81EA\u52A8\u6D4B\u901F\u5E76\u53EF\u624B\u52A8\u5207\u6362").action(async (apiKey, options) => {
7920
+ await gmnCommand(apiKey, options.platform, options.name, options.baseUrl);
7600
7921
  });
7601
- program.command("gmn1 [apiKey]").description("\u914D\u7F6E GMN1 \u5230 Codex\u3001OpenCode\u3001OpenClaw\uFF08\u9ED8\u8BA4 URL: https://gmncode.cn\uFF09").option("-p, --platform <platforms>", "\u6307\u5B9A\u5E73\u53F0 (codex,opencode,openclaw,all)").option("-n, --name <providerName>", "\u6307\u5B9A\u670D\u52A1\u5546\u540D\u79F0\uFF08\u9ED8\u8BA4: gmn\uFF09").action(async (apiKey, options) => {
7602
- await gmn1Command(apiKey, options.platform, options.name);
7922
+ program.command("gmn1 [apiKey]").alias("gmncode").description("\u914D\u7F6E GMN1 \u5230 Codex\u3001OpenCode\u3001OpenClaw\uFF08\u6D4B\u901F\u5E76\u9009\u62E9\u6700\u4F4E\u5EF6\u8FDF\u8DEF\u7EBF\uFF09").option("-p, --platform <platforms>", "\u6307\u5B9A\u5E73\u53F0 (codex,opencode,openclaw,all)").option("-n, --name <providerName>", "\u6307\u5B9A\u670D\u52A1\u5546\u540D\u79F0\uFF08\u9ED8\u8BA4: gmn\uFF09").option("-b, --base-url <baseUrl>", "\u6307\u5B9A Base URL\uFF1B\u4E0D\u6307\u5B9A\u65F6\u81EA\u52A8\u6D4B\u901F\u5E76\u53EF\u624B\u52A8\u5207\u6362").action(async (apiKey, options) => {
7923
+ await gmn1Command(apiKey, options.platform, options.name, options.baseUrl);
7603
7924
  });
7604
7925
  (async () => {
7605
7926
  if (!process.argv.slice(2).length) {