codexmate 0.0.9 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +25 -6
  2. package/README.zh-CN.md +25 -6
  3. package/package.json +53 -36
  4. package/res/logo.png +0 -0
  5. package/{cli.js → src/cli.js} +822 -327
  6. package/src/lib/cli-file-utils.js +151 -0
  7. package/src/lib/cli-models-utils.js +152 -0
  8. package/src/lib/cli-network-utils.js +148 -0
  9. package/src/lib/cli-session-utils.js +121 -0
  10. package/src/lib/cli-utils.js +139 -0
  11. package/src/res/json5.min.js +1 -0
  12. package/src/res/logo.png +0 -0
  13. package/src/res/screenshot.png +0 -0
  14. package/src/res/vue.global.js +18552 -0
  15. package/src/web-ui/app.js +2970 -0
  16. package/src/web-ui/index.html +1310 -0
  17. package/src/web-ui/logic.mjs +157 -0
  18. package/src/web-ui/styles.css +2868 -0
  19. package/src/web-ui.html +17 -0
  20. package/web-ui/app.js +273 -144
  21. package/web-ui/index.html +1310 -0
  22. package/web-ui/logic.mjs +21 -21
  23. package/web-ui/styles.css +2868 -0
  24. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
  25. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
  26. package/.github/workflows/ci.yml +0 -26
  27. package/.github/workflows/release.yml +0 -159
  28. package/.planning/.fix-attempts +0 -1
  29. package/.planning/.lock +0 -6
  30. package/.planning/.verify-cache.json +0 -14
  31. package/.planning/CHECKPOINT.json +0 -46
  32. package/.planning/DESIGN.md +0 -26
  33. package/.planning/HISTORY.json +0 -124
  34. package/.planning/PLAN.md +0 -69
  35. package/.planning/REVIEW.md +0 -41
  36. package/.planning/STATE.md +0 -12
  37. package/.planning/STATS.json +0 -13
  38. package/.planning/VERIFICATION.md +0 -70
  39. package/.planning/daude-code-plan.md +0 -51
  40. package/.planning/research/architecture.md +0 -32
  41. package/.planning/research/conventions.md +0 -36
  42. package/.planning/task_1-REVIEW.md +0 -29
  43. package/.planning/task_1-SUMMARY.md +0 -32
  44. package/.planning/task_2-REVIEW.md +0 -24
  45. package/.planning/task_2-SUMMARY.md +0 -37
  46. package/.planning/task_3-REVIEW.md +0 -25
  47. package/.planning/task_3-SUMMARY.md +0 -31
  48. package/cmd/publish-npm.cmd +0 -65
  49. package/tests/e2e/helpers.js +0 -214
  50. package/tests/e2e/recent-health.e2e.js +0 -142
  51. package/tests/e2e/run.js +0 -154
  52. package/tests/e2e/test-claude.js +0 -21
  53. package/tests/e2e/test-config.js +0 -124
  54. package/tests/e2e/test-health-speed.js +0 -79
  55. package/tests/e2e/test-openclaw.js +0 -47
  56. package/tests/e2e/test-session-search.js +0 -114
  57. package/tests/e2e/test-sessions.js +0 -69
  58. package/tests/e2e/test-setup.js +0 -159
  59. package/tests/unit/run.mjs +0 -29
  60. package/tests/unit/web-ui-logic.test.mjs +0 -186
  61. package/web-ui.html +0 -3977
  62. /package/{CHANGELOG.md → doc/CHANGELOG.md} +0 -0
  63. /package/{CHANGELOG.zh-CN.md → doc/CHANGELOG.zh-CN.md} +0 -0
package/web-ui/app.js CHANGED
@@ -58,6 +58,7 @@
58
58
  currentProvider: '',
59
59
  currentModel: '',
60
60
  serviceTier: 'fast',
61
+ modelReasoningEffort: 'high',
61
62
  providersList: [],
62
63
  models: [],
63
64
  codexModelsLoading: false,
@@ -80,8 +81,10 @@
80
81
  showOpenclawConfigModal: false,
81
82
  showConfigTemplateModal: false,
82
83
  showAgentsModal: false,
84
+ showInstallModal: false,
83
85
  configTemplateContent: '',
84
86
  configTemplateApplying: false,
87
+ codexApplying: false,
85
88
  agentsContent: '',
86
89
  agentsPath: '',
87
90
  agentsExists: false,
@@ -91,9 +94,9 @@
91
94
  agentsContext: 'codex',
92
95
  agentsModalTitle: 'AGENTS.md 编辑器',
93
96
  agentsModalHint: '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
94
- sessionsList: [],
95
- sessionsLoading: false,
96
- sessionFilterSource: 'codex',
97
+ sessionsList: [],
98
+ sessionsLoading: false,
99
+ sessionFilterSource: 'all',
97
100
  sessionPathFilter: '',
98
101
  sessionQuery: '',
99
102
  sessionRoleFilter: 'all',
@@ -134,7 +137,12 @@
134
137
  claudeSpeedLoading: {},
135
138
  claudeShareLoading: {},
136
139
  providerShareLoading: {},
140
+ installCommands: [
141
+ 'npm install -g @anthropic-ai/claude-code',
142
+ 'npm i -g @openai/codex'
143
+ ],
137
144
  newProvider: { name: '', url: '', key: '' },
145
+ resetConfigLoading: false,
138
146
  editingProvider: { name: '', url: '', key: '' },
139
147
  newModelName: '',
140
148
  currentClaudeConfig: '',
@@ -248,19 +256,19 @@
248
256
  this.loadAll();
249
257
  },
250
258
 
251
- computed: {
252
- isSessionQueryEnabled() {
253
- return isSessionQueryEnabled(this.sessionFilterSource);
254
- },
255
- sessionQueryPlaceholder() {
256
- if (this.isSessionQueryEnabled) {
257
- return '关键词检索(支持 Codex/Claude,例:claude code)';
258
- }
259
- return '当前来源暂不支持关键词检索';
260
- },
261
- claudeModelHasList() {
262
- return Array.isArray(this.claudeModels) && this.claudeModels.length > 0;
263
- },
259
+ computed: {
260
+ isSessionQueryEnabled() {
261
+ return isSessionQueryEnabled(this.sessionFilterSource);
262
+ },
263
+ sessionQueryPlaceholder() {
264
+ if (this.isSessionQueryEnabled) {
265
+ return '关键词检索(支持 Codex/Claude,例:claude code)';
266
+ }
267
+ return '当前来源暂不支持关键词检索';
268
+ },
269
+ claudeModelHasList() {
270
+ return Array.isArray(this.claudeModels) && this.claudeModels.length > 0;
271
+ },
264
272
  claudeModelOptions() {
265
273
  const list = Array.isArray(this.claudeModels) ? [...this.claudeModels] : [];
266
274
  const current = (this.currentClaudeModel || '').trim();
@@ -275,8 +283,7 @@
275
283
  this.loading = true;
276
284
  this.initError = '';
277
285
  try {
278
- const statusRes = await api('status');
279
- const listRes = await api('list');
286
+ const [statusRes, listRes] = await Promise.all([api('status'), api('list')]);
280
287
 
281
288
  if (statusRes.error) {
282
289
  this.initError = statusRes.error;
@@ -289,13 +296,18 @@
289
296
  : '';
290
297
  this.serviceTier = tier === 'fast' ? 'fast' : (tier ? 'standard' : 'fast');
291
298
  }
299
+ {
300
+ const effort = typeof statusRes.modelReasoningEffort === 'string'
301
+ ? statusRes.modelReasoningEffort.trim().toLowerCase()
302
+ : '';
303
+ this.modelReasoningEffort = effort || 'high';
304
+ }
292
305
  this.providersList = listRes.providers;
293
- await this.loadModelsForProvider(this.currentProvider);
294
306
  if (statusRes.configReady === false) {
295
- this.showMessage(statusRes.configNotice || '未检测到 config.toml,已加载默认模板。请在模板编辑器确认后创建。', 'info');
307
+ this.showMessage('配置已加载', 'info');
296
308
  }
297
309
  if (statusRes.initNotice) {
298
- this.showMessage(statusRes.initNotice, 'info');
310
+ this.showMessage('配置就绪', 'info');
299
311
  }
300
312
  this.maybeShowStarPrompt();
301
313
  }
@@ -304,6 +316,13 @@
304
316
  } finally {
305
317
  this.loading = false;
306
318
  }
319
+
320
+ // 模型加载单独异步,不阻塞主 loading
321
+ try {
322
+ await this.loadModelsForProvider(this.currentProvider);
323
+ } catch (e) {
324
+ // loadModelsForProvider 内部已有 toast,这里吞掉防止抛出
325
+ }
307
326
  },
308
327
 
309
328
  async loadModelsForProvider(providerName) {
@@ -324,7 +343,7 @@
324
343
  return;
325
344
  }
326
345
  if (res.error) {
327
- this.showMessage('模型列表获取失败: ' + res.error, 'error');
346
+ this.showMessage('获取模型列表失败', 'error');
328
347
  this.models = [];
329
348
  this.modelsSource = 'error';
330
349
  this.modelsHasCurrent = true;
@@ -335,7 +354,7 @@
335
354
  this.modelsSource = res.source || 'remote';
336
355
  this.modelsHasCurrent = !!this.currentModel && list.includes(this.currentModel);
337
356
  } catch (e) {
338
- this.showMessage('模型列表获取失败: ' + e.message, 'error');
357
+ this.showMessage('获取模型列表失败', 'error');
339
358
  this.models = [];
340
359
  this.modelsSource = 'error';
341
360
  this.modelsHasCurrent = true;
@@ -380,7 +399,7 @@
380
399
  const res = await api('get-claude-settings');
381
400
  if (res && res.error) {
382
401
  if (!silent) {
383
- this.showMessage('读取 Claude 配置失败: ' + res.error, 'error');
402
+ this.showMessage('读取配置失败', 'error');
384
403
  }
385
404
  return;
386
405
  }
@@ -403,7 +422,7 @@
403
422
  }
404
423
  } catch (e) {
405
424
  if (!silent) {
406
- this.showMessage('读取 Claude 配置失败: ' + e.message, 'error');
425
+ this.showMessage('读取配置失败', 'error');
407
426
  }
408
427
  }
409
428
  },
@@ -454,7 +473,7 @@
454
473
  return;
455
474
  }
456
475
  if (res.error) {
457
- this.showMessage('模型列表获取失败: ' + res.error, 'error');
476
+ this.showMessage('获取模型列表失败', 'error');
458
477
  this.claudeModels = [];
459
478
  this.claudeModelsSource = 'error';
460
479
  this.claudeModelsHasCurrent = true;
@@ -465,7 +484,7 @@
465
484
  this.claudeModelsSource = res.source || 'remote';
466
485
  this.updateClaudeModelsCurrent();
467
486
  } catch (e) {
468
- this.showMessage('模型列表获取失败: ' + e.message, 'error');
487
+ this.showMessage('获取模型列表失败', 'error');
469
488
  this.claudeModels = [];
470
489
  this.claudeModelsSource = 'error';
471
490
  this.claudeModelsHasCurrent = true;
@@ -483,7 +502,7 @@
483
502
  if (localStorage.getItem(storageKey)) {
484
503
  return;
485
504
  }
486
- this.showMessage('如果 Codex Mate 对你有帮助,欢迎到 GitHub 点个 Star', 'info');
505
+ this.showMessage('欢迎到 GitHub Star', 'info');
487
506
  localStorage.setItem(storageKey, '1');
488
507
  },
489
508
 
@@ -593,7 +612,7 @@
593
612
  openSessionStandalone(session) {
594
613
  const url = this.buildSessionStandaloneUrl(session);
595
614
  if (!url) {
596
- this.showMessage('当前会话无法生成新页链接', 'error');
615
+ this.showMessage('无法生成链接', 'error');
597
616
  return;
598
617
  }
599
618
  window.open(url, '_blank', 'noopener');
@@ -674,38 +693,52 @@
674
693
  copyAgentsContent() {
675
694
  const text = typeof this.agentsContent === 'string' ? this.agentsContent : '';
676
695
  if (!text) {
677
- this.showMessage('没有可复制的内容', 'info');
696
+ this.showMessage('没有可复制内容', 'info');
678
697
  return;
679
698
  }
680
699
  const ok = this.fallbackCopyText(text);
681
700
  if (ok) {
682
- this.showMessage('已复制 AGENTS.md 内容', 'success');
701
+ this.showMessage('已复制', 'success');
683
702
  return;
684
703
  }
685
- this.showMessage('复制失败,请手动复制内容', 'error');
704
+ this.showMessage('复制失败', 'error');
705
+ },
706
+
707
+ copyInstallCommand(cmd) {
708
+ const text = typeof cmd === 'string' ? cmd.trim() : '';
709
+ if (!text) {
710
+ this.showMessage('没有可复制内容', 'info');
711
+ return;
712
+ }
713
+ const ok = this.fallbackCopyText(text);
714
+ if (ok) {
715
+ this.showMessage('已复制命令', 'success');
716
+ return;
717
+ }
718
+ this.showMessage('复制失败', 'error');
686
719
  },
687
720
 
688
721
  async copyResumeCommand(session) {
689
722
  if (!this.isResumeCommandAvailable(session)) {
690
- this.showMessage('当前会话不支持生成恢复命令', 'error');
723
+ this.showMessage('不支持此操作', 'error');
691
724
  return;
692
725
  }
693
726
  const command = this.buildResumeCommand(session);
694
727
  const ok = this.fallbackCopyText(command);
695
728
  if (ok) {
696
- this.showMessage('已复制恢复命令', 'success');
729
+ this.showMessage('已复制', 'success');
697
730
  return;
698
731
  }
699
732
  try {
700
733
  if (navigator.clipboard && window.isSecureContext) {
701
734
  await navigator.clipboard.writeText(command);
702
- this.showMessage('已复制恢复命令', 'success');
735
+ this.showMessage('已复制', 'success');
703
736
  return;
704
737
  }
705
738
  } catch (e) {
706
739
  // keep fallback failure message
707
740
  }
708
- this.showMessage('复制失败,请手动复制命令', 'error');
741
+ this.showMessage('复制失败', 'error');
709
742
  },
710
743
 
711
744
  buildProviderShareCommand(payload) {
@@ -742,7 +775,7 @@
742
775
  async copyProviderShareCommand(provider) {
743
776
  const name = provider && typeof provider.name === 'string' ? provider.name.trim() : '';
744
777
  if (!name) {
745
- this.showMessage('提供商名称无效', 'error');
778
+ this.showMessage('参数无效', 'error');
746
779
  return;
747
780
  }
748
781
  if (this.providerShareLoading[name]) {
@@ -757,26 +790,26 @@
757
790
  }
758
791
  const command = this.buildProviderShareCommand(res && res.payload ? res.payload : null);
759
792
  if (!command) {
760
- this.showMessage('分享命令生成失败', 'error');
793
+ this.showMessage('生成命令失败', 'error');
761
794
  return;
762
795
  }
763
796
  const ok = this.fallbackCopyText(command);
764
797
  if (ok) {
765
- this.showMessage('已复制分享命令', 'success');
798
+ this.showMessage('已复制', 'success');
766
799
  return;
767
800
  }
768
801
  try {
769
802
  if (navigator.clipboard && window.isSecureContext) {
770
803
  await navigator.clipboard.writeText(command);
771
- this.showMessage('已复制分享命令', 'success');
804
+ this.showMessage('已复制', 'success');
772
805
  return;
773
806
  }
774
807
  } catch (e) {
775
808
  // keep fallback failure message
776
809
  }
777
- this.showMessage('复制失败,请手动复制命令', 'error');
810
+ this.showMessage('复制失败', 'error');
778
811
  } catch (e) {
779
- this.showMessage('生成分享命令失败: ' + e.message, 'error');
812
+ this.showMessage('生成命令失败', 'error');
780
813
  } finally {
781
814
  this.providerShareLoading[name] = false;
782
815
  }
@@ -798,26 +831,26 @@
798
831
  }
799
832
  const command = this.buildClaudeShareCommand(res && res.payload ? res.payload : null);
800
833
  if (!command) {
801
- this.showMessage('分享命令生成失败', 'error');
834
+ this.showMessage('生成命令失败', 'error');
802
835
  return;
803
836
  }
804
837
  const ok = this.fallbackCopyText(command);
805
838
  if (ok) {
806
- this.showMessage('已复制分享命令', 'success');
839
+ this.showMessage('已复制', 'success');
807
840
  return;
808
841
  }
809
842
  try {
810
843
  if (navigator.clipboard && window.isSecureContext) {
811
844
  await navigator.clipboard.writeText(command);
812
- this.showMessage('已复制分享命令', 'success');
845
+ this.showMessage('已复制', 'success');
813
846
  return;
814
847
  }
815
848
  } catch (e) {
816
849
  // fall through
817
850
  }
818
- this.showMessage('复制失败,请手动复制命令', 'error');
851
+ this.showMessage('复制失败', 'error');
819
852
  } catch (e) {
820
- this.showMessage('生成分享命令失败: ' + e.message, 'error');
853
+ this.showMessage('生成命令失败', 'error');
821
854
  } finally {
822
855
  this.claudeShareLoading[name] = false;
823
856
  }
@@ -825,7 +858,7 @@
825
858
 
826
859
  async cloneSession(session) {
827
860
  if (!this.isCloneAvailable(session)) {
828
- this.showMessage('当前会话不支持克隆', 'error');
861
+ this.showMessage('不支持此操作', 'error');
829
862
  return;
830
863
  }
831
864
  const key = this.getSessionExportKey(session);
@@ -844,7 +877,7 @@
844
877
  return;
845
878
  }
846
879
 
847
- this.showMessage('会话已克隆', 'success');
880
+ this.showMessage('操作成功', 'success');
848
881
  await this.loadSessions();
849
882
  if (res.sessionId) {
850
883
  const matched = this.sessionsList.find(item => item.source === 'codex' && item.sessionId === res.sessionId);
@@ -853,7 +886,7 @@
853
886
  }
854
887
  }
855
888
  } catch (e) {
856
- this.showMessage('克隆失败: ' + e.message, 'error');
889
+ this.showMessage('克隆失败', 'error');
857
890
  } finally {
858
891
  this.sessionCloning[key] = false;
859
892
  }
@@ -861,7 +894,7 @@
861
894
 
862
895
  async deleteSession(session) {
863
896
  if (!this.isDeleteAvailable(session)) {
864
- this.showMessage('当前会话不支持删除', 'error');
897
+ this.showMessage('不支持此操作', 'error');
865
898
  return;
866
899
  }
867
900
  const key = this.getSessionExportKey(session);
@@ -879,10 +912,10 @@
879
912
  this.showMessage(res.error, 'error');
880
913
  return;
881
914
  }
882
- this.showMessage('会话已删除', 'success');
915
+ this.showMessage('操作成功', 'success');
883
916
  await this.loadSessions();
884
917
  } catch (e) {
885
- this.showMessage('删除失败: ' + e.message, 'error');
918
+ this.showMessage('删除失败', 'error');
886
919
  } finally {
887
920
  this.sessionDeleting[key] = false;
888
921
  }
@@ -893,7 +926,7 @@
893
926
  return value.trim();
894
927
  },
895
928
 
896
- mergeSessionPathOptions(baseList = [], incomingList = []) {
929
+ mergeSessionPathOptions(baseList = [], incomingList = []) {
897
930
  const merged = [];
898
931
  const seen = new Set();
899
932
  const append = (items) => {
@@ -931,8 +964,8 @@
931
964
  return paths;
932
965
  },
933
966
 
934
- syncSessionPathOptionsForSource(source, nextOptions, mergeWithExisting = false) {
935
- const targetSource = source === 'claude' ? 'claude' : 'codex';
967
+ syncSessionPathOptionsForSource(source, nextOptions, mergeWithExisting = false) {
968
+ const targetSource = source === 'claude' ? 'claude' : (source === 'all' ? 'all' : 'codex');
936
969
  const current = Array.isArray(this.sessionPathOptionsMap[targetSource])
937
970
  ? this.sessionPathOptionsMap[targetSource]
938
971
  : [];
@@ -944,10 +977,10 @@
944
977
  [targetSource]: merged
945
978
  };
946
979
  this.refreshSessionPathOptions(targetSource);
947
- },
948
-
949
- refreshSessionPathOptions(source) {
950
- const targetSource = source === 'claude' ? 'claude' : 'codex';
980
+ },
981
+
982
+ refreshSessionPathOptions(source) {
983
+ const targetSource = source === 'claude' ? 'claude' : (source === 'all' ? 'all' : 'codex');
951
984
  const base = Array.isArray(this.sessionPathOptionsMap[targetSource])
952
985
  ? [...this.sessionPathOptionsMap[targetSource]]
953
986
  : [];
@@ -960,8 +993,8 @@
960
993
  }
961
994
  },
962
995
 
963
- async loadSessionPathOptions(options = {}) {
964
- const source = options.source === 'claude' ? 'claude' : 'codex';
996
+ async loadSessionPathOptions(options = {}) {
997
+ const source = options.source === 'claude' ? 'claude' : (options.source === 'all' ? 'all' : 'codex');
965
998
  const forceRefresh = !!options.forceRefresh;
966
999
  const loaded = !!this.sessionPathOptionsLoadedMap[source];
967
1000
  if (!forceRefresh && loaded) {
@@ -1013,12 +1046,12 @@
1013
1046
  await this.loadSessions();
1014
1047
  },
1015
1048
 
1016
- async clearSessionFilters() {
1017
- this.sessionFilterSource = 'codex';
1018
- this.sessionPathFilter = '';
1019
- this.sessionQuery = '';
1020
- this.sessionRoleFilter = 'all';
1021
- this.sessionTimePreset = 'all';
1049
+ async clearSessionFilters() {
1050
+ this.sessionFilterSource = 'all';
1051
+ this.sessionPathFilter = '';
1052
+ this.sessionQuery = '';
1053
+ this.sessionRoleFilter = 'all';
1054
+ this.sessionTimePreset = 'all';
1022
1055
  await this.onSessionSourceChange();
1023
1056
  },
1024
1057
 
@@ -1093,7 +1126,7 @@
1093
1126
  this.activeSession = null;
1094
1127
  this.activeSessionMessages = [];
1095
1128
  this.activeSessionDetailClipped = false;
1096
- this.showMessage('加载会话失败: ' + e.message, 'error');
1129
+ this.showMessage('加载会话失败', 'error');
1097
1130
  } finally {
1098
1131
  this.sessionsLoading = false;
1099
1132
  }
@@ -1224,7 +1257,9 @@
1224
1257
  },
1225
1258
 
1226
1259
  downloadTextFile(fileName, content) {
1227
- const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
1260
+ // 使用 UTF-8 BOM 确保文本编辑器正确识别编码
1261
+ const BOM = '\uFEFF';
1262
+ const blob = new Blob([BOM + content], { type: 'text/markdown;charset=utf-8' });
1228
1263
  const url = URL.createObjectURL(blob);
1229
1264
  const link = document.createElement('a');
1230
1265
  link.href = url;
@@ -1255,10 +1290,10 @@
1255
1290
  const maxLabel = res.maxMessages === 'all' ? 'all' : res.maxMessages;
1256
1291
  this.showMessage(`会话导出完成(已截断:最多 ${maxLabel} 条消息)`, 'info');
1257
1292
  } else {
1258
- this.showMessage('会话导出完成', 'success');
1293
+ this.showMessage('操作成功', 'success');
1259
1294
  }
1260
1295
  } catch (e) {
1261
- this.showMessage('导出失败: ' + e.message, 'error');
1296
+ this.showMessage('导出失败', 'error');
1262
1297
  } finally {
1263
1298
  this.sessionExporting[key] = false;
1264
1299
  }
@@ -1267,15 +1302,22 @@
1267
1302
  async switchProvider(name) {
1268
1303
  this.currentProvider = name;
1269
1304
  await this.loadModelsForProvider(name);
1270
- await this.openConfigTemplateEditor();
1305
+ if (this.modelsSource === 'remote' && this.models.length > 0 && !this.models.includes(this.currentModel)) {
1306
+ this.currentModel = this.models[0];
1307
+ }
1308
+ await this.applyCodexConfigDirect({ silent: true });
1271
1309
  },
1272
1310
 
1273
1311
  async onModelChange() {
1274
- await this.openConfigTemplateEditor();
1312
+ await this.applyCodexConfigDirect();
1275
1313
  },
1276
1314
 
1277
1315
  async onServiceTierChange() {
1278
- await this.openConfigTemplateEditor();
1316
+ await this.applyCodexConfigDirect({ silent: true });
1317
+ },
1318
+
1319
+ async onReasoningEffortChange() {
1320
+ await this.applyCodexConfigDirect({ silent: true });
1279
1321
  },
1280
1322
 
1281
1323
  async runHealthCheck() {
@@ -1320,15 +1362,15 @@
1320
1362
  remote
1321
1363
  };
1322
1364
  if (ok) {
1323
- this.showMessage('健康检查通过', 'success');
1365
+ this.showMessage('检查通过', 'success');
1324
1366
  }
1325
1367
  } else {
1326
1368
  this.healthCheckResult = null;
1327
- this.showMessage('健康检查失败:返回数据异常', 'error');
1369
+ this.showMessage('检查失败', 'error');
1328
1370
  }
1329
1371
  } catch (e) {
1330
1372
  this.healthCheckResult = null;
1331
- this.showMessage('健康检查失败: ' + e.message, 'error');
1373
+ this.showMessage('检查失败', 'error');
1332
1374
  } finally {
1333
1375
  if (this.configMode === 'claude') {
1334
1376
  try {
@@ -1369,7 +1411,50 @@
1369
1411
  this.configTemplateContent = template;
1370
1412
  this.showConfigTemplateModal = true;
1371
1413
  } catch (e) {
1372
- this.showMessage('加载模板失败: ' + e.message, 'error');
1414
+ this.showMessage('加载模板失败', 'error');
1415
+ }
1416
+ },
1417
+
1418
+ async applyCodexConfigDirect(options = {}) {
1419
+ if (this.codexApplying) return;
1420
+
1421
+ const provider = (this.currentProvider || '').trim();
1422
+ const model = (this.currentModel || '').trim();
1423
+ if (!provider || !model) {
1424
+ this.showMessage('请选择提供商和模型', 'error');
1425
+ return;
1426
+ }
1427
+
1428
+ this.codexApplying = true;
1429
+ try {
1430
+ const tplRes = await api('get-config-template', {
1431
+ provider,
1432
+ model,
1433
+ serviceTier: this.serviceTier,
1434
+ reasoningEffort: this.modelReasoningEffort
1435
+ });
1436
+ if (tplRes.error) {
1437
+ this.showMessage('获取模板失败', 'error');
1438
+ return;
1439
+ }
1440
+
1441
+ const applyRes = await api('apply-config-template', {
1442
+ template: tplRes.template
1443
+ });
1444
+ if (applyRes.error) {
1445
+ this.showMessage('应用模板失败', 'error');
1446
+ return;
1447
+ }
1448
+
1449
+ if (options.silent !== true) {
1450
+ this.showMessage('配置已应用', 'success');
1451
+ }
1452
+
1453
+ await this.loadAll();
1454
+ } catch (e) {
1455
+ this.showMessage('应用失败', 'error');
1456
+ } finally {
1457
+ this.codexApplying = false;
1373
1458
  }
1374
1459
  },
1375
1460
 
@@ -1380,7 +1465,7 @@
1380
1465
 
1381
1466
  async applyConfigTemplate() {
1382
1467
  if (!this.configTemplateContent || !this.configTemplateContent.trim()) {
1383
- this.showMessage('模板内容不能为空', 'error');
1468
+ this.showMessage('模板不能为空', 'error');
1384
1469
  return;
1385
1470
  }
1386
1471
 
@@ -1393,11 +1478,11 @@
1393
1478
  this.showMessage(res.error, 'error');
1394
1479
  return;
1395
1480
  }
1396
- this.showMessage('模板已应用到 config.toml', 'success');
1481
+ this.showMessage('模板已应用', 'success');
1397
1482
  this.closeConfigTemplateModal();
1398
1483
  await this.loadAll();
1399
1484
  } catch (e) {
1400
- this.showMessage('应用模板失败: ' + e.message, 'error');
1485
+ this.showMessage('应用模板失败', 'error');
1401
1486
  } finally {
1402
1487
  this.configTemplateApplying = false;
1403
1488
  }
@@ -1418,7 +1503,7 @@
1418
1503
  this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
1419
1504
  this.showAgentsModal = true;
1420
1505
  } catch (e) {
1421
- this.showMessage('加载 AGENTS.md 失败: ' + e.message, 'error');
1506
+ this.showMessage('加载文件失败', 'error');
1422
1507
  } finally {
1423
1508
  this.agentsLoading = false;
1424
1509
  }
@@ -1442,7 +1527,7 @@
1442
1527
  this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
1443
1528
  this.showAgentsModal = true;
1444
1529
  } catch (e) {
1445
- this.showMessage('加载 OpenClaw AGENTS.md 失败: ' + e.message, 'error');
1530
+ this.showMessage('加载文件失败', 'error');
1446
1531
  } finally {
1447
1532
  this.agentsLoading = false;
1448
1533
  }
@@ -1451,7 +1536,7 @@
1451
1536
  async openOpenclawWorkspaceEditor() {
1452
1537
  const fileName = (this.openclawWorkspaceFileName || '').trim();
1453
1538
  if (!fileName) {
1454
- this.showMessage('请输入工作区文件名', 'error');
1539
+ this.showMessage('请输入文件名', 'error');
1455
1540
  return;
1456
1541
  }
1457
1542
  this.setAgentsModalContext('openclaw-workspace', { fileName });
@@ -1471,7 +1556,7 @@
1471
1556
  this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
1472
1557
  this.showAgentsModal = true;
1473
1558
  } catch (e) {
1474
- this.showMessage('加载 OpenClaw 工作区文件失败: ' + e.message, 'error');
1559
+ this.showMessage('加载文件失败', 'error');
1475
1560
  } finally {
1476
1561
  this.agentsLoading = false;
1477
1562
  }
@@ -1533,7 +1618,7 @@
1533
1618
  this.showMessage(successLabel, 'success');
1534
1619
  this.closeAgentsModal();
1535
1620
  } catch (e) {
1536
- this.showMessage('保存文件失败: ' + e.message, 'error');
1621
+ this.showMessage('保存失败', 'error');
1537
1622
  } finally {
1538
1623
  this.agentsSaving = false;
1539
1624
  }
@@ -1548,29 +1633,40 @@
1548
1633
  return this.showMessage('名称不能为空', 'error');
1549
1634
  }
1550
1635
  if (this.providersList.some(item => item.name === name)) {
1551
- return this.showMessage('提供商已存在', 'error');
1636
+ return this.showMessage('名称已存在', 'error');
1552
1637
  }
1553
1638
 
1554
- const safeName = this.escapeTomlString(name);
1555
- const safeUrl = this.escapeTomlString(this.newProvider.url.trim());
1556
- const safeKey = this.escapeTomlString(this.newProvider.key || '');
1557
- const newProviderBlock = `[model_providers.${safeName}]\nname = "${safeName}"\nbase_url = "${safeUrl}"\nwire_api = "responses"\nrequires_openai_auth = false\npreferred_auth_method = "${safeKey}"\nrequest_max_retries = 4\nstream_max_retries = 10\nstream_idle_timeout_ms = 300000`;
1639
+ try {
1640
+ const res = await api('add-provider', {
1641
+ name,
1642
+ url: this.newProvider.url.trim(),
1643
+ key: this.newProvider.key || ''
1644
+ });
1645
+ if (res.error) {
1646
+ this.showMessage(res.error, 'error');
1647
+ return;
1648
+ }
1558
1649
 
1559
- this.currentProvider = name;
1560
- this.showMessage('已生成新增模板,请确认后应用', 'info');
1561
- this.closeAddModal();
1562
- await this.openConfigTemplateEditor({
1563
- appendHint: `新增 provider: ${name}(请检查字段后应用)`,
1564
- appendBlock: newProviderBlock
1565
- });
1650
+ this.showMessage('操作成功', 'success');
1651
+ this.closeAddModal();
1652
+ await this.loadAll();
1653
+ } catch (e) {
1654
+ this.showMessage('添加失败', 'error');
1655
+ }
1566
1656
  },
1567
1657
 
1568
1658
  async deleteProvider(name) {
1569
- if (!confirm(`确定删除提供商 "${name}"?`)) return;
1570
- this.showMessage('请在模板中手动删除该 provider 配置块后应用', 'info');
1571
- await this.openConfigTemplateEditor({
1572
- appendHint: `请手动删除 [model_providers.${name}] 配置块,并确认 model_provider 指向有效 provider`
1573
- });
1659
+ const res = await api('delete-provider', { name });
1660
+ if (res.error) {
1661
+ this.showMessage(res.error, 'error');
1662
+ return;
1663
+ }
1664
+ if (res.switched && res.provider) {
1665
+ this.showMessage(`已删除提供商,自动切换到 ${res.provider}${res.model ? ` / ${res.model}` : ''}`, 'success');
1666
+ } else {
1667
+ this.showMessage('操作成功', 'success');
1668
+ }
1669
+ await this.loadAll();
1574
1670
  },
1575
1671
 
1576
1672
  openEditModal(provider) {
@@ -1588,13 +1684,20 @@
1588
1684
  }
1589
1685
 
1590
1686
  const name = this.editingProvider.name;
1591
- const safeUrl = this.escapeTomlString(this.editingProvider.url.trim());
1592
- const safeKey = this.escapeTomlString(this.editingProvider.key || '');
1687
+ const url = this.editingProvider.url.trim();
1688
+ const key = this.editingProvider.key || '';
1593
1689
  this.closeEditModal();
1594
- this.showMessage('已生成更新模板,请确认后应用', 'info');
1595
- await this.openConfigTemplateEditor({
1596
- appendHint: `请将 [model_providers.${name}] 中 base_url 更新为 ${safeUrl}${safeKey ? ',并更新 preferred_auth_method' : ''}`
1597
- });
1690
+ try {
1691
+ const res = await api('update-provider', { name, url, key });
1692
+ if (res.error) {
1693
+ this.showMessage(res.error, 'error');
1694
+ return;
1695
+ }
1696
+ this.showMessage('操作成功', 'success');
1697
+ await this.loadAll();
1698
+ } catch (e) {
1699
+ this.showMessage('更新失败', 'error');
1700
+ }
1598
1701
  },
1599
1702
 
1600
1703
  closeEditModal() {
@@ -1602,27 +1705,45 @@
1602
1705
  this.editingProvider = { name: '', url: '', key: '' };
1603
1706
  },
1604
1707
 
1708
+ async resetConfig() {
1709
+ if (this.resetConfigLoading) return;
1710
+ this.resetConfigLoading = true;
1711
+ try {
1712
+ const res = await api('reset-config');
1713
+ if (res.error) {
1714
+ this.showMessage(res.error, 'error');
1715
+ return;
1716
+ }
1717
+ const backup = res.backupFile ? `(已备份: ${res.backupFile})` : '';
1718
+ this.showMessage(`配置已重装${backup}`, 'success');
1719
+ await this.loadAll();
1720
+ } catch (e) {
1721
+ this.showMessage('重装失败', 'error');
1722
+ } finally {
1723
+ this.resetConfigLoading = false;
1724
+ }
1725
+ },
1726
+
1605
1727
  async addModel() {
1606
1728
  if (!this.newModelName || !this.newModelName.trim()) {
1607
- return this.showMessage('请输入模型名称', 'error');
1729
+ return this.showMessage('请输入模型', 'error');
1608
1730
  }
1609
1731
  const res = await api('add-model', { model: this.newModelName.trim() });
1610
1732
  if (res.error) {
1611
1733
  this.showMessage(res.error, 'error');
1612
1734
  } else {
1613
- this.showMessage('已添加', 'success');
1735
+ this.showMessage('操作成功', 'success');
1614
1736
  this.closeModelModal();
1615
1737
  await this.loadAll();
1616
1738
  }
1617
1739
  },
1618
1740
 
1619
1741
  async removeModel(model) {
1620
- if (!confirm(`确定删除模型 "${model}"?`)) return;
1621
1742
  const res = await api('delete-model', { model });
1622
1743
  if (res.error) {
1623
1744
  this.showMessage(res.error, 'error');
1624
1745
  } else {
1625
- this.showMessage('已删除', 'success');
1746
+ this.showMessage('操作成功', 'success');
1626
1747
  await this.loadAll();
1627
1748
  }
1628
1749
  },
@@ -1677,7 +1798,7 @@
1677
1798
  this.saveClaudeConfigs();
1678
1799
  this.updateClaudeModelsCurrent();
1679
1800
  if (!this.claudeConfigs[name].apiKey) {
1680
- this.showMessage('该配置未设置 API Key,请先编辑', 'error');
1801
+ this.showMessage('请先配置 API Key', 'error');
1681
1802
  return;
1682
1803
  }
1683
1804
  this.applyClaudeConfig(name);
@@ -1707,7 +1828,7 @@
1707
1828
  hasKey: !!this.editingConfig.apiKey
1708
1829
  };
1709
1830
  this.saveClaudeConfigs();
1710
- this.showMessage('配置已更新', 'success');
1831
+ this.showMessage('操作成功', 'success');
1711
1832
  this.closeEditConfigModal();
1712
1833
  if (name === this.currentClaudeConfig) {
1713
1834
  this.refreshClaudeModelContext();
@@ -1731,7 +1852,7 @@
1731
1852
 
1732
1853
  const config = this.claudeConfigs[name];
1733
1854
  if (!config.apiKey) {
1734
- this.showMessage('已保存,未应用:请先输入 API Key', 'info');
1855
+ this.showMessage('已保存,未应用', 'info');
1735
1856
  this.closeEditConfigModal();
1736
1857
  if (name === this.currentClaudeConfig) {
1737
1858
  this.refreshClaudeModelContext();
@@ -1741,7 +1862,7 @@
1741
1862
 
1742
1863
  const res = await api('apply-claude-config', { config });
1743
1864
  if (res.error || res.success === false) {
1744
- this.showMessage(res.error || '应用 Claude 配置失败', 'error');
1865
+ this.showMessage(res.error || '应用配置失败', 'error');
1745
1866
  } else {
1746
1867
  const targetTip = res.targetPath ? `(${res.targetPath})` : '';
1747
1868
  this.showMessage(`已保存并应用到 Claude 配置${targetTip}`, 'success');
@@ -1754,15 +1875,15 @@
1754
1875
 
1755
1876
  addClaudeConfig() {
1756
1877
  if (!this.newClaudeConfig.name || !this.newClaudeConfig.name.trim()) {
1757
- return this.showMessage('请输入配置名称', 'error');
1878
+ return this.showMessage('请输入名称', 'error');
1758
1879
  }
1759
1880
  const name = this.newClaudeConfig.name.trim();
1760
1881
  if (this.claudeConfigs[name]) {
1761
- return this.showMessage('配置名称已存在', 'error');
1882
+ return this.showMessage('名称已存在', 'error');
1762
1883
  }
1763
1884
  const duplicateName = this.findDuplicateClaudeConfigName(this.newClaudeConfig);
1764
1885
  if (duplicateName) {
1765
- return this.showMessage('已存在相同配置,已忽略添加', 'info');
1886
+ return this.showMessage('配置已存在', 'info');
1766
1887
  }
1767
1888
 
1768
1889
  this.claudeConfigs[name] = {
@@ -1774,14 +1895,14 @@
1774
1895
 
1775
1896
  this.currentClaudeConfig = name;
1776
1897
  this.saveClaudeConfigs();
1777
- this.showMessage('配置已添加', 'success');
1898
+ this.showMessage('操作成功', 'success');
1778
1899
  this.closeClaudeConfigModal();
1779
1900
  this.refreshClaudeModelContext();
1780
1901
  },
1781
1902
 
1782
1903
  deleteClaudeConfig(name) {
1783
1904
  if (Object.keys(this.claudeConfigs).length <= 1) {
1784
- return this.showMessage('至少保留一个配置', 'error');
1905
+ return this.showMessage('至少保留一项', 'error');
1785
1906
  }
1786
1907
 
1787
1908
  if (!confirm(`确定删除配置 "${name}"?`)) return;
@@ -1791,7 +1912,7 @@
1791
1912
  this.currentClaudeConfig = Object.keys(this.claudeConfigs)[0];
1792
1913
  }
1793
1914
  this.saveClaudeConfigs();
1794
- this.showMessage('配置已删除', 'success');
1915
+ this.showMessage('操作成功', 'success');
1795
1916
  this.refreshClaudeModelContext();
1796
1917
  },
1797
1918
 
@@ -1801,12 +1922,12 @@
1801
1922
  const config = this.claudeConfigs[name];
1802
1923
 
1803
1924
  if (!config.apiKey) {
1804
- return this.showMessage('该配置未设置 API Key,请先编辑', 'error');
1925
+ return this.showMessage('请先配置 API Key', 'error');
1805
1926
  }
1806
1927
 
1807
1928
  const res = await api('apply-claude-config', { config });
1808
1929
  if (res.error || res.success === false) {
1809
- this.showMessage(res.error || '应用 Claude 配置失败', 'error');
1930
+ this.showMessage(res.error || '应用配置失败', 'error');
1810
1931
  } else {
1811
1932
  const targetTip = res.targetPath ? `(${res.targetPath})` : '';
1812
1933
  this.showMessage(`已应用配置到 Claude 设置: ${name}${targetTip}`, 'success');
@@ -2014,7 +2135,7 @@
2014
2135
  }
2015
2136
  this.fillOpenclawQuickFromConfig(parsed.data);
2016
2137
  if (!silent) {
2017
- this.showMessage('已从编辑器读取快速配置', 'success');
2138
+ this.showMessage('已读取配置', 'success');
2018
2139
  }
2019
2140
  return true;
2020
2141
  },
@@ -2110,7 +2231,7 @@
2110
2231
  this.refreshOpenclawProviders(parsed.data);
2111
2232
  this.refreshOpenclawAgentsList(parsed.data);
2112
2233
  if (!silent) {
2113
- this.showMessage('已从文本刷新结构化配置', 'success');
2234
+ this.showMessage('已刷新配置', 'success');
2114
2235
  }
2115
2236
  return true;
2116
2237
  },
@@ -2408,7 +2529,7 @@
2408
2529
  this.refreshOpenclawProviders(config);
2409
2530
  this.refreshOpenclawAgentsList(config);
2410
2531
  this.fillOpenclawQuickFromConfig(config);
2411
- this.showMessage('已写入编辑器', 'success');
2532
+ this.showMessage('已写入', 'success');
2412
2533
  },
2413
2534
 
2414
2535
  applyOpenclawQuickToText() {
@@ -2421,7 +2542,7 @@
2421
2542
  const providerName = (this.openclawQuick.providerName || '').trim();
2422
2543
  const modelId = (this.openclawQuick.modelId || '').trim();
2423
2544
  if (!providerName) {
2424
- this.showMessage('请填写 Provider 名称', 'error');
2545
+ this.showMessage('请填写名称', 'error');
2425
2546
  return;
2426
2547
  }
2427
2548
  if (providerName.includes('/')) {
@@ -2429,7 +2550,7 @@
2429
2550
  return;
2430
2551
  }
2431
2552
  if (!modelId) {
2432
- this.showMessage('请填写模型 ID', 'error');
2553
+ this.showMessage('请填写模型', 'error');
2433
2554
  return;
2434
2555
  }
2435
2556
 
@@ -2440,7 +2561,7 @@
2440
2561
  const provider = ensureObject(providers[providerName]);
2441
2562
  const baseUrl = (this.openclawQuick.baseUrl || '').trim();
2442
2563
  if (!baseUrl && !provider.baseUrl) {
2443
- this.showMessage('请填写 Base URL', 'error');
2564
+ this.showMessage('请填写 URL', 'error');
2444
2565
  return;
2445
2566
  }
2446
2567
 
@@ -2526,7 +2647,7 @@
2526
2647
  this.fillOpenclawStructured(config);
2527
2648
  this.refreshOpenclawProviders(config);
2528
2649
  this.refreshOpenclawAgentsList(config);
2529
- this.showMessage('快速配置已写入编辑器', 'success');
2650
+ this.showMessage('配置已写入', 'success');
2530
2651
  },
2531
2652
 
2532
2653
  addOpenclawFallback() {
@@ -2630,6 +2751,14 @@
2630
2751
  this.resetOpenclawQuick();
2631
2752
  },
2632
2753
 
2754
+ openInstallModal() {
2755
+ this.showInstallModal = true;
2756
+ },
2757
+
2758
+ closeInstallModal() {
2759
+ this.showInstallModal = false;
2760
+ },
2761
+
2633
2762
  async loadOpenclawConfigFromFile(options = {}) {
2634
2763
  const silent = !!options.silent;
2635
2764
  const force = !!options.force;
@@ -2655,11 +2784,11 @@
2655
2784
  }
2656
2785
  this.syncOpenclawStructuredFromText({ silent: true });
2657
2786
  if (!silent) {
2658
- this.showMessage('已加载当前 OpenClaw 配置', 'success');
2787
+ this.showMessage('加载完成', 'success');
2659
2788
  }
2660
2789
  } catch (e) {
2661
2790
  if (!silent) {
2662
- this.showMessage('加载 OpenClaw 配置失败: ' + e.message, 'error');
2791
+ this.showMessage('加载配置失败', 'error');
2663
2792
  }
2664
2793
  } finally {
2665
2794
  this.openclawFileLoading = false;
@@ -2668,12 +2797,12 @@
2668
2797
 
2669
2798
  persistOpenclawConfig({ closeModal = true } = {}) {
2670
2799
  if (!this.openclawEditing.name || !this.openclawEditing.name.trim()) {
2671
- this.showMessage('请输入配置名称', 'error');
2800
+ this.showMessage('请输入名称', 'error');
2672
2801
  return '';
2673
2802
  }
2674
2803
  const name = this.openclawEditing.name.trim();
2675
2804
  if (!this.openclawEditing.lockName && this.openclawConfigs[name]) {
2676
- this.showMessage('配置名称已存在', 'error');
2805
+ this.showMessage('名称已存在', 'error');
2677
2806
  return '';
2678
2807
  }
2679
2808
  if (!this.openclawEditing.content || !this.openclawEditing.content.trim()) {
@@ -2697,7 +2826,7 @@
2697
2826
  try {
2698
2827
  const name = this.persistOpenclawConfig();
2699
2828
  if (!name) return;
2700
- this.showMessage('OpenClaw 配置已保存', 'success');
2829
+ this.showMessage('操作成功', 'success');
2701
2830
  } finally {
2702
2831
  this.openclawSaving = false;
2703
2832
  }
@@ -2714,7 +2843,7 @@
2714
2843
  lineEnding: this.openclawLineEnding
2715
2844
  });
2716
2845
  if (res.error || res.success === false) {
2717
- this.showMessage(res.error || '应用 OpenClaw 配置失败', 'error');
2846
+ this.showMessage(res.error || '应用配置失败', 'error');
2718
2847
  return;
2719
2848
  }
2720
2849
  this.openclawConfigPath = res.targetPath || this.openclawConfigPath;
@@ -2723,7 +2852,7 @@
2723
2852
  this.showMessage(`已保存并应用 OpenClaw 配置${targetTip}`, 'success');
2724
2853
  this.closeOpenclawConfigModal();
2725
2854
  } catch (e) {
2726
- this.showMessage('应用 OpenClaw 配置失败: ' + e.message, 'error');
2855
+ this.showMessage('应用配置失败', 'error');
2727
2856
  } finally {
2728
2857
  this.openclawApplying = false;
2729
2858
  }
@@ -2731,7 +2860,7 @@
2731
2860
 
2732
2861
  deleteOpenclawConfig(name) {
2733
2862
  if (Object.keys(this.openclawConfigs).length <= 1) {
2734
- return this.showMessage('至少保留一个配置', 'error');
2863
+ return this.showMessage('至少保留一项', 'error');
2735
2864
  }
2736
2865
  if (!confirm(`确定删除配置 "${name}"?`)) return;
2737
2866
  delete this.openclawConfigs[name];
@@ -2739,21 +2868,21 @@
2739
2868
  this.currentOpenclawConfig = Object.keys(this.openclawConfigs)[0];
2740
2869
  }
2741
2870
  this.saveOpenclawConfigs();
2742
- this.showMessage('OpenClaw 配置已删除', 'success');
2871
+ this.showMessage('操作成功', 'success');
2743
2872
  },
2744
2873
 
2745
2874
  async applyOpenclawConfig(name) {
2746
2875
  this.currentOpenclawConfig = name;
2747
2876
  const config = this.openclawConfigs[name];
2748
2877
  if (!this.openclawHasContent(config)) {
2749
- return this.showMessage('该配置为空,请先编辑', 'error');
2878
+ return this.showMessage('配置为空', 'error');
2750
2879
  }
2751
2880
  const res = await api('apply-openclaw-config', {
2752
2881
  content: config.content,
2753
2882
  lineEnding: this.openclawLineEnding
2754
2883
  });
2755
2884
  if (res.error || res.success === false) {
2756
- this.showMessage(res.error || '应用 OpenClaw 配置失败', 'error');
2885
+ this.showMessage(res.error || '应用配置失败', 'error');
2757
2886
  } else {
2758
2887
  this.openclawConfigPath = res.targetPath || this.openclawConfigPath;
2759
2888
  this.openclawConfigExists = true;