ccman 3.3.16 → 3.3.18

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.16",
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,19 +7216,187 @@ import fs13 from "fs";
7151
7216
  import path15 from "path";
7152
7217
  import chalk54 from "chalk";
7153
7218
  import inquirer38 from "inquirer";
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/commands/gmn.ts
7154
7334
  var DEFAULT_PROVIDER_NAME2 = "gmn";
7155
7335
  var VALID_PLATFORMS = ["codex", "opencode", "openclaw"];
7156
7336
  var DEFAULT_PLATFORMS = ["codex", "opencode"];
7157
- var GMN_OPENAI_BASE_URL = "https://gmn.chuangzuoli.com";
7158
- var GMN_OPENCLAW_BASE_URL = "https://gmn.chuangzuoli.com/v1";
7159
- var TOTAL_STEPS = 3;
7337
+ var GMN_BASE_URLS = [
7338
+ {
7339
+ label: "\u539F\u59CB\u5730\u5740",
7340
+ url: "https://gmn.chuangzuoli.com",
7341
+ description: "GMN \u539F\u59CB\u5165\u53E3"
7342
+ },
7343
+ {
7344
+ label: "\u65E7\u57DF\u540D CDN",
7345
+ url: "https://cdn.gmnchuangzuoli.com",
7346
+ description: "CDN \u56DE\u56FD\u52A0\u901F"
7347
+ },
7348
+ {
7349
+ label: "\u963F\u91CC\u4E91 CDN",
7350
+ url: "https://gmncodex.com",
7351
+ description: "\u963F\u91CC\u4E91\u89E3\u6790 CDN \u56DE\u56FD\u52A0\u901F"
7352
+ },
7353
+ {
7354
+ label: "\u5168\u7403\u8FB9\u7F18 A",
7355
+ url: "https://gmncode.cn",
7356
+ description: "\u5168\u7403\u8FB9\u7F18\u8282\u70B9\u52A0\u901F"
7357
+ },
7358
+ {
7359
+ label: "CF CDN A",
7360
+ url: "https://cdn.gmncode.cn",
7361
+ description: "CF \u89E3\u6790 CDN \u56DE\u56FD\u52A0\u901F"
7362
+ },
7363
+ {
7364
+ label: "\u5168\u7403\u8FB9\u7F18 B",
7365
+ url: "https://gmn.codex.com",
7366
+ description: "\u5168\u7403\u8FB9\u7F18\u8282\u70B9\u52A0\u901F"
7367
+ },
7368
+ {
7369
+ label: "CF CDN B",
7370
+ url: "https://cdn.gmncode.com",
7371
+ description: "CF \u89E3\u6790 CDN \u56DE\u56FD\u52A0\u901F"
7372
+ }
7373
+ ];
7374
+ var GMN_PROFILE = {
7375
+ commandName: "gmn",
7376
+ title: "GMN",
7377
+ baseUrls: GMN_BASE_URLS
7378
+ };
7379
+ var GMN1_PROFILE = {
7380
+ commandName: "gmn1",
7381
+ title: "GMN1",
7382
+ baseUrls: [
7383
+ {
7384
+ label: "GMN1 \u9ED8\u8BA4\u7EBF\u8DEF",
7385
+ url: "https://gmncode.cn",
7386
+ description: "\u5168\u7403\u8FB9\u7F18\u8282\u70B9\u52A0\u901F"
7387
+ }
7388
+ ]
7389
+ };
7390
+ var TOTAL_STEPS = 4;
7391
+ var BASE_URL_PROBE_SAMPLE_COUNT = 3;
7392
+ var BASE_URL_PROBE_TIMEOUT_MS = 2500;
7160
7393
  function renderStep(current, total, title) {
7161
7394
  const barLength = total;
7162
7395
  const filledLength = Math.min(current, total);
7163
7396
  const bar = `${"\u25A0".repeat(filledLength)}${"\u25A1".repeat(barLength - filledLength)}`;
7164
7397
  return `\u6B65\u9AA4 ${current}/${total} [${bar}] ${title}`;
7165
7398
  }
7166
- function printBanner() {
7399
+ function printBanner(title) {
7167
7400
  printLogo();
7168
7401
  console.log(
7169
7402
  chalk54.cyanBright(
@@ -7174,7 +7407,7 @@ function printBanner() {
7174
7407
  " \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551",
7175
7408
  " \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551",
7176
7409
  " \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D",
7177
- " CCMAN GMN \u4E00\u952E\u914D\u7F6E\u5411\u5BFC"
7410
+ ` CCMAN ${title} \u4E00\u952E\u914D\u7F6E\u5411\u5BFC`
7178
7411
  ].join("\n")
7179
7412
  )
7180
7413
  );
@@ -7225,12 +7458,12 @@ function parsePlatforms(platformArg) {
7225
7458
  }
7226
7459
  return platforms;
7227
7460
  }
7228
- async function promptApiKey() {
7461
+ async function promptApiKey(title) {
7229
7462
  const answers = await inquirer38.prompt([
7230
7463
  {
7231
7464
  type: "password",
7232
7465
  name: "apiKey",
7233
- message: "\u8BF7\u8F93\u5165 GMN API Key:",
7466
+ message: `\u8BF7\u8F93\u5165 ${title} API Key:`,
7234
7467
  mask: "*",
7235
7468
  validate: (value) => {
7236
7469
  if (!value?.trim()) return "API Key \u4E0D\u80FD\u4E3A\u7A7A";
@@ -7240,7 +7473,7 @@ async function promptApiKey() {
7240
7473
  ]);
7241
7474
  return answers.apiKey.trim();
7242
7475
  }
7243
- async function promptPlatforms() {
7476
+ async function promptPlatforms(title) {
7244
7477
  const answers = await inquirer38.prompt([
7245
7478
  {
7246
7479
  type: "checkbox",
@@ -7250,7 +7483,7 @@ async function promptPlatforms() {
7250
7483
  choices: [
7251
7484
  { name: "Codex\uFF08\u9700\u5355\u72EC\u8BA2\u9605 OpenAI \u5957\u9910\uFF09", value: "codex" },
7252
7485
  { name: "OpenCode\uFF08\u4E0E Codex \u5171\u4EAB OpenAI \u5957\u9910\uFF09", value: "opencode" },
7253
- { name: "OpenClaw\uFF08GMN /v1 \u7AEF\u70B9\uFF0C\u9ED8\u8BA4\u4E0D\u9009\u4E2D\uFF09", value: "openclaw" },
7486
+ { name: `OpenClaw\uFF08${title} /v1 \u7AEF\u70B9\uFF0C\u9ED8\u8BA4\u4E0D\u9009\u4E2D\uFF09`, value: "openclaw" },
7254
7487
  { name: "\u5168\u90E8\uFF08\u5C06\u4F9D\u6B21\u914D\u7F6E Codex\u3001OpenCode\u3001OpenClaw\uFF09", value: "all" }
7255
7488
  ],
7256
7489
  default: DEFAULT_PLATFORMS,
@@ -7266,11 +7499,83 @@ async function promptPlatforms() {
7266
7499
  }
7267
7500
  return selected;
7268
7501
  }
7269
- async function resolvePlatforms(platformArg) {
7502
+ async function resolvePlatforms(platformArg, title = "GMN") {
7270
7503
  if (platformArg && platformArg.trim().length > 0) {
7271
7504
  return parsePlatforms(platformArg);
7272
7505
  }
7273
- return promptPlatforms();
7506
+ return promptPlatforms(title);
7507
+ }
7508
+ function formatLatency(result) {
7509
+ if (result.latencyMs === null) {
7510
+ return result.error || "\u6D4B\u901F\u5931\u8D25";
7511
+ }
7512
+ return `${result.latencyMs} ms`;
7513
+ }
7514
+ function buildProbeCandidates(baseUrls, platforms) {
7515
+ const openClawOnly = platforms.length === 1 && platforms[0] === "openclaw";
7516
+ return baseUrls.map((item) => ({
7517
+ ...item,
7518
+ probeUrl: openClawOnly ? buildOpenClawBaseUrl(item.url) : item.url
7519
+ }));
7520
+ }
7521
+ function printBaseUrlProbeResults(results, platforms) {
7522
+ const usingOpenClawPath = platforms.length === 1 && platforms[0] === "openclaw";
7523
+ console.log(
7524
+ chalk54.gray(
7525
+ `\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" : ""}`
7526
+ )
7527
+ );
7528
+ for (const result of results) {
7529
+ const latencyText = result.latencyMs === null ? chalk54.red(formatLatency(result)) : chalk54.green(formatLatency(result));
7530
+ console.log(` ${chalk54.cyan(result.label)} \xB7 ${latencyText}`);
7531
+ console.log(chalk54.gray(` ${result.url}`));
7532
+ console.log(chalk54.gray(` ${result.description}`));
7533
+ }
7534
+ }
7535
+ async function resolveOpenAiBaseUrl(profile, platforms, baseUrlArg) {
7536
+ if (baseUrlArg && baseUrlArg.trim().length > 0) {
7537
+ const normalized = normalizeEndpointUrl(baseUrlArg);
7538
+ console.log(chalk54.gray(`\u5DF2\u901A\u8FC7\u53C2\u6570\u6307\u5B9A Base URL: ${normalized}`));
7539
+ return normalized;
7540
+ }
7541
+ const probeResults = sortEndpointProbeResults(
7542
+ await probeEndpointCandidates(buildProbeCandidates(profile.baseUrls, platforms), {
7543
+ sampleCount: BASE_URL_PROBE_SAMPLE_COUNT,
7544
+ timeoutMs: BASE_URL_PROBE_TIMEOUT_MS
7545
+ })
7546
+ );
7547
+ if (probeResults.length === 0) {
7548
+ throw new Error("\u6CA1\u6709\u53EF\u7528\u7684 Base URL \u5019\u9009\u9879");
7549
+ }
7550
+ printBaseUrlProbeResults(probeResults, platforms);
7551
+ const defaultResult = pickDefaultEndpoint(probeResults) || probeResults[0];
7552
+ const allFailed = probeResults.every((result) => result.latencyMs === null);
7553
+ if (allFailed) {
7554
+ 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"));
7555
+ } else {
7556
+ console.log(chalk54.gray(`\u9ED8\u8BA4\u5DF2\u9009\u5EF6\u8FDF\u6700\u4F4E\u5730\u5740\uFF1A${defaultResult.url}`));
7557
+ }
7558
+ if (probeResults.length === 1) {
7559
+ console.log(chalk54.green(`\u5DF2\u81EA\u52A8\u9009\u62E9: ${defaultResult.url}`));
7560
+ return defaultResult.url;
7561
+ }
7562
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
7563
+ console.log(chalk54.yellow(`\u975E\u4EA4\u4E92\u73AF\u5883\uFF0C\u5DF2\u81EA\u52A8\u4F7F\u7528\u9ED8\u8BA4\u5730\u5740\uFF1A${defaultResult.url}`));
7564
+ return defaultResult.url;
7565
+ }
7566
+ const answers = await inquirer38.prompt([
7567
+ {
7568
+ type: "list",
7569
+ name: "baseUrl",
7570
+ message: "\u9009\u62E9\u8981\u4F7F\u7528\u7684 Base URL\uFF08\u9ED8\u8BA4\u5DF2\u9009\u5EF6\u8FDF\u6700\u4F4E\uFF09:",
7571
+ choices: probeResults.map((result) => ({
7572
+ name: `${result.label} \xB7 ${result.url} \xB7 ${formatLatency(result)} \xB7 ${result.description}`,
7573
+ value: result.url
7574
+ })),
7575
+ default: defaultResult.url
7576
+ }
7577
+ ]);
7578
+ return answers.baseUrl;
7274
7579
  }
7275
7580
  function resolveProviderName2(providerNameArg) {
7276
7581
  if (providerNameArg === void 0) {
@@ -7357,14 +7662,18 @@ function rollbackFromBackupOrThrow(result) {
7357
7662
  throw new Error(`\u56DE\u6EDA\u5931\u8D25: ${errors.join("; ")}`);
7358
7663
  }
7359
7664
  }
7360
- async function gmnCommand(apiKey, platformArg, providerNameArg) {
7361
- printBanner();
7665
+ function buildOpenClawBaseUrl(openaiBaseUrl) {
7666
+ const normalized = openaiBaseUrl.replace(/\/+$/, "");
7667
+ return `${normalized}/v1`;
7668
+ }
7669
+ async function runGmnCommand(profile, apiKey, platformArg, providerNameArg, baseUrlArg) {
7670
+ printBanner(profile.title);
7362
7671
  let platforms;
7363
7672
  let providerName;
7364
7673
  try {
7365
7674
  console.log(chalk54.cyan(`
7366
7675
  ${renderStep(1, TOTAL_STEPS, "\u9009\u62E9\u8981\u914D\u7F6E\u7684\u5DE5\u5177")}`));
7367
- platforms = await resolvePlatforms(platformArg);
7676
+ platforms = await resolvePlatforms(platformArg, profile.title);
7368
7677
  providerName = resolveProviderName2(providerNameArg);
7369
7678
  } catch (error) {
7370
7679
  console.error(chalk54.red(`\u274C ${error.message}`));
@@ -7373,11 +7682,26 @@ ${renderStep(1, TOTAL_STEPS, "\u9009\u62E9\u8981\u914D\u7F6E\u7684\u5DE5\u5177")
7373
7682
  console.log(chalk54.gray(`\u5DF2\u9009\u62E9: ${platforms.join(", ")}`));
7374
7683
  console.log(chalk54.gray(`\u670D\u52A1\u5546\u540D\u79F0: ${providerName}`));
7375
7684
  printKeyNotice();
7685
+ console.log(chalk54.cyan(`
7686
+ ${renderStep(2, TOTAL_STEPS, "\u6D4B\u901F\u5E76\u9009\u62E9 Base URL")}`));
7687
+ let openaiBaseUrl;
7688
+ try {
7689
+ openaiBaseUrl = await resolveOpenAiBaseUrl(profile, platforms, baseUrlArg);
7690
+ } catch (error) {
7691
+ console.error(chalk54.red(`\u274C ${error.message}`));
7692
+ process.exit(1);
7693
+ }
7694
+ const openclawBaseUrl = buildOpenClawBaseUrl(openaiBaseUrl);
7695
+ const platformBaseUrls = {
7696
+ codex: openaiBaseUrl,
7697
+ opencode: openaiBaseUrl,
7698
+ openclaw: openclawBaseUrl
7699
+ };
7376
7700
  let resolvedApiKey = apiKey?.trim();
7377
7701
  console.log(chalk54.cyan(`
7378
- ${renderStep(2, TOTAL_STEPS, "\u8F93\u5165 API Key")}`));
7702
+ ${renderStep(3, TOTAL_STEPS, "\u8F93\u5165 API Key")}`));
7379
7703
  if (!resolvedApiKey) {
7380
- resolvedApiKey = await promptApiKey();
7704
+ resolvedApiKey = await promptApiKey(profile.title);
7381
7705
  } else {
7382
7706
  console.log(chalk54.gray("\u5DF2\u901A\u8FC7\u53C2\u6570\u63D0\u4F9B API Key\uFF08\u5DF2\u9690\u85CF\uFF09"));
7383
7707
  }
@@ -7385,27 +7709,19 @@ ${renderStep(2, TOTAL_STEPS, "\u8F93\u5165 API Key")}`));
7385
7709
  console.error(chalk54.red("\u274C \u9519\u8BEF: API Key \u4E0D\u80FD\u4E3A\u7A7A"));
7386
7710
  process.exit(1);
7387
7711
  }
7388
- const openaiBaseUrl = GMN_OPENAI_BASE_URL;
7389
- const platformBaseUrls = {
7390
- codex: openaiBaseUrl,
7391
- opencode: openaiBaseUrl,
7392
- openclaw: GMN_OPENCLAW_BASE_URL
7393
- };
7394
7712
  console.log(chalk54.cyan(`
7395
- ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7713
+ ${renderStep(4, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7396
7714
  console.log(chalk54.gray(`\u5DF2\u9009\u62E9\u5E73\u53F0: ${platforms.join(", ")}`));
7397
- if (platforms.includes("codex") || platforms.includes("opencode")) {
7398
- console.log(chalk54.gray(`OpenAI Base URL: ${openaiBaseUrl}`));
7399
- }
7715
+ console.log(chalk54.gray(`OpenAI Base URL: ${openaiBaseUrl}`));
7400
7716
  if (platforms.includes("openclaw")) {
7401
- console.log(chalk54.gray(`OpenClaw Base URL: ${GMN_OPENCLAW_BASE_URL}`));
7717
+ console.log(chalk54.gray(`OpenClaw Base URL: ${openclawBaseUrl}`));
7402
7718
  }
7403
7719
  printWriteTargets(platforms);
7404
7720
  console.log();
7405
7721
  const backupRootDir = path15.join(
7406
7722
  getCcmanDir(),
7407
7723
  "backups",
7408
- `gmn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
7724
+ `${profile.commandName}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
7409
7725
  );
7410
7726
  fs13.mkdirSync(backupRootDir, { recursive: true, mode: 448 });
7411
7727
  console.log(chalk54.gray(`\u5907\u4EFD\u6839\u76EE\u5F55: ${backupRootDir}`));
@@ -7434,8 +7750,12 @@ ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7434
7750
  }
7435
7751
  const baseUrl = platformBaseUrls[platform];
7436
7752
  const existing = findPreferredProvider(manager.list(), providerName);
7437
- const provider = existing ? manager.edit(existing.id, { name: providerName, baseUrl, apiKey: resolvedApiKey }) : manager.add({ name: providerName, baseUrl, apiKey: resolvedApiKey });
7438
- manager.switch(provider.id);
7753
+ const provider = existing ? manager.edit(
7754
+ existing.id,
7755
+ { name: providerName, baseUrl, apiKey: resolvedApiKey },
7756
+ { applyWrite: false }
7757
+ ) : manager.add({ name: providerName, baseUrl, apiKey: resolvedApiKey });
7758
+ manager.switch(provider.id, { mode: "overwrite" });
7439
7759
  completed += 1;
7440
7760
  console.log(chalk54.green(`\u2705 ${name}`));
7441
7761
  successBackups.push({
@@ -7462,7 +7782,7 @@ ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7462
7782
  }
7463
7783
  }
7464
7784
  console.log(chalk54.green(`
7465
- \u{1F389} GMN \u914D\u7F6E\u5B8C\u6210\uFF01(${completed}/${tools.length})`));
7785
+ \u{1F389} ${profile.title} \u914D\u7F6E\u5B8C\u6210\uFF01(${completed}/${tools.length})`));
7466
7786
  console.log();
7467
7787
  console.log(chalk54.bold("\u5907\u4EFD\u4FE1\u606F:"));
7468
7788
  if (successBackups.length === 0) {
@@ -7481,6 +7801,12 @@ ${renderStep(3, TOTAL_STEPS, "\u5F00\u59CB\u5199\u5165\u914D\u7F6E")}`));
7481
7801
  }
7482
7802
  console.log(chalk54.gray("\u63D0\u793A\uFF1A\u8BF7\u91CD\u542F\u5BF9\u5E94\u5DE5\u5177/\u63D2\u4EF6\u4EE5\u4F7F\u914D\u7F6E\u751F\u6548\u3002"));
7483
7803
  }
7804
+ async function gmnCommand(apiKey, platformArg, providerNameArg, baseUrlArg) {
7805
+ await runGmnCommand(GMN_PROFILE, apiKey, platformArg, providerNameArg, baseUrlArg);
7806
+ }
7807
+ async function gmn1Command(apiKey, platformArg, providerNameArg, baseUrlArg) {
7808
+ await runGmnCommand(GMN1_PROFILE, apiKey, platformArg, providerNameArg, baseUrlArg);
7809
+ }
7484
7810
 
7485
7811
  // src/index.ts
7486
7812
  init_dist2();
@@ -7516,7 +7842,9 @@ program.on("command:*", (operands) => {
7516
7842
  "sync",
7517
7843
  "export",
7518
7844
  "import",
7519
- "gmn"
7845
+ "gmn",
7846
+ "gmn1",
7847
+ "gmncode"
7520
7848
  ];
7521
7849
  const suggestions = availableCommands.filter(
7522
7850
  (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
@@ -7575,8 +7903,11 @@ sync.action(async () => {
7575
7903
  });
7576
7904
  exportCommand(program);
7577
7905
  importCommand(program);
7578
- 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) => {
7579
- await gmnCommand(apiKey, options.platform, options.name);
7906
+ program.command("gmn [apiKey]").description("\u914D\u7F6E GMN \u5230 Codex\u3001OpenCode\u3001OpenClaw\uFF08\u81EA\u52A8\u6D4B\u901F\u5E76\u9ED8\u8BA4\u9009\u62E9\u6700\u4F4E\u5EF6\u8FDF\u7EBF\u8DEF\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) => {
7907
+ await gmnCommand(apiKey, options.platform, options.name, options.baseUrl);
7908
+ });
7909
+ program.command("gmn1 [apiKey]").alias("gmncode").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").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) => {
7910
+ await gmn1Command(apiKey, options.platform, options.name, options.baseUrl);
7580
7911
  });
7581
7912
  (async () => {
7582
7913
  if (!process.argv.slice(2).length) {