codexmate 0.0.21 → 0.0.22

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 (114) hide show
  1. package/README.md +389 -284
  2. package/README.zh.md +321 -0
  3. package/cli/agents-files.js +224 -162
  4. package/cli/archive-helpers.js +446 -446
  5. package/cli/auth-profiles.js +359 -359
  6. package/cli/builtin-proxy.js +1044 -580
  7. package/cli/claude-proxy.js +998 -998
  8. package/cli/config-bootstrap.js +384 -384
  9. package/cli/config-health.js +338 -338
  10. package/cli/openai-bridge.js +950 -0
  11. package/cli/openclaw-config.js +629 -629
  12. package/cli/session-usage.concurrent.js +28 -0
  13. package/cli/session-usage.js +112 -0
  14. package/cli/session-usage.models.js +176 -0
  15. package/cli/skills.js +1141 -1141
  16. package/cli/zip-commands.js +510 -510
  17. package/cli.js +13214 -13129
  18. package/lib/cli-file-utils.js +151 -151
  19. package/lib/cli-models-utils.js +419 -419
  20. package/lib/cli-network-utils.js +164 -164
  21. package/lib/cli-path-utils.js +69 -69
  22. package/lib/cli-session-utils.js +121 -121
  23. package/lib/cli-sessions.js +386 -386
  24. package/lib/cli-utils.js +155 -155
  25. package/lib/download-artifacts.js +77 -77
  26. package/lib/mcp-stdio.js +440 -440
  27. package/lib/task-orchestrator.js +869 -869
  28. package/lib/text-diff.js +303 -303
  29. package/lib/workflow-engine.js +340 -340
  30. package/package.json +74 -74
  31. package/res/json5.min.js +1 -1
  32. package/res/logo.png +0 -0
  33. package/res/vue.global.prod.js +13 -13
  34. package/web-ui/app.js +575 -530
  35. package/web-ui/index.html +34 -33
  36. package/web-ui/logic.agents-diff.mjs +386 -386
  37. package/web-ui/logic.claude.mjs +168 -168
  38. package/web-ui/logic.mjs +5 -5
  39. package/web-ui/logic.runtime.mjs +128 -124
  40. package/web-ui/logic.sessions.mjs +614 -581
  41. package/web-ui/modules/api.mjs +90 -90
  42. package/web-ui/modules/app.computed.dashboard.mjs +126 -113
  43. package/web-ui/modules/app.computed.index.mjs +17 -15
  44. package/web-ui/modules/app.computed.main-tabs.mjs +198 -195
  45. package/web-ui/modules/app.computed.session.mjs +653 -507
  46. package/web-ui/modules/app.constants.mjs +15 -15
  47. package/web-ui/modules/app.methods.agents.mjs +544 -493
  48. package/web-ui/modules/app.methods.claude-config.mjs +174 -174
  49. package/web-ui/modules/app.methods.codex-config.mjs +795 -640
  50. package/web-ui/modules/app.methods.index.mjs +92 -88
  51. package/web-ui/modules/app.methods.install.mjs +161 -149
  52. package/web-ui/modules/app.methods.navigation.mjs +619 -619
  53. package/web-ui/modules/app.methods.openclaw-core.mjs +814 -814
  54. package/web-ui/modules/app.methods.openclaw-editing.mjs +372 -372
  55. package/web-ui/modules/app.methods.openclaw-persist.mjs +369 -369
  56. package/web-ui/modules/app.methods.providers.mjs +404 -363
  57. package/web-ui/modules/app.methods.runtime.mjs +323 -323
  58. package/web-ui/modules/app.methods.session-actions.mjs +537 -520
  59. package/web-ui/modules/app.methods.session-browser.mjs +626 -626
  60. package/web-ui/modules/app.methods.session-timeline.mjs +448 -448
  61. package/web-ui/modules/app.methods.session-trash.mjs +422 -422
  62. package/web-ui/modules/app.methods.startup-claude.mjs +405 -412
  63. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -471
  64. package/web-ui/modules/config-mode.computed.mjs +126 -126
  65. package/web-ui/modules/config-template-confirm-pref.mjs +33 -0
  66. package/web-ui/modules/i18n.mjs +1609 -0
  67. package/web-ui/modules/plugins.computed.mjs +220 -0
  68. package/web-ui/modules/plugins.methods.mjs +620 -0
  69. package/web-ui/modules/plugins.storage.mjs +37 -0
  70. package/web-ui/modules/skills.computed.mjs +107 -107
  71. package/web-ui/modules/skills.methods.mjs +481 -481
  72. package/web-ui/partials/index/layout-footer.html +13 -13
  73. package/web-ui/partials/index/layout-header.html +461 -402
  74. package/web-ui/partials/index/modal-config-template-agents.html +175 -125
  75. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  76. package/web-ui/partials/index/modal-health-check.html +72 -72
  77. package/web-ui/partials/index/modal-openclaw-config.html +280 -280
  78. package/web-ui/partials/index/modal-skills.html +200 -184
  79. package/web-ui/partials/index/modals-basic.html +165 -156
  80. package/web-ui/partials/index/panel-config-claude.html +138 -126
  81. package/web-ui/partials/index/panel-config-codex.html +234 -237
  82. package/web-ui/partials/index/panel-config-openclaw.html +78 -78
  83. package/web-ui/partials/index/panel-docs.html +147 -130
  84. package/web-ui/partials/index/panel-market.html +174 -174
  85. package/web-ui/partials/index/panel-orchestration.html +397 -397
  86. package/web-ui/partials/index/panel-plugins.html +243 -0
  87. package/web-ui/partials/index/panel-sessions.html +292 -292
  88. package/web-ui/partials/index/panel-settings.html +258 -190
  89. package/web-ui/partials/index/panel-usage.html +353 -213
  90. package/web-ui/session-helpers.mjs +573 -559
  91. package/web-ui/source-bundle.cjs +233 -233
  92. package/web-ui/styles/base-theme.css +264 -271
  93. package/web-ui/styles/controls-forms.css +362 -360
  94. package/web-ui/styles/docs-panel.css +247 -182
  95. package/web-ui/styles/feedback.css +108 -108
  96. package/web-ui/styles/health-check-dialog.css +144 -144
  97. package/web-ui/styles/layout-shell.css +596 -376
  98. package/web-ui/styles/modals-core.css +464 -464
  99. package/web-ui/styles/navigation-panels.css +382 -348
  100. package/web-ui/styles/openclaw-structured.css +266 -266
  101. package/web-ui/styles/plugins-panel.css +518 -0
  102. package/web-ui/styles/responsive.css +456 -450
  103. package/web-ui/styles/sessions-list.css +400 -400
  104. package/web-ui/styles/sessions-preview.css +411 -411
  105. package/web-ui/styles/sessions-toolbar-trash.css +268 -243
  106. package/web-ui/styles/sessions-usage.css +851 -628
  107. package/web-ui/styles/settings-panel.css +166 -0
  108. package/web-ui/styles/skills-list.css +303 -296
  109. package/web-ui/styles/skills-market.css +396 -335
  110. package/web-ui/styles/task-orchestration.css +776 -776
  111. package/web-ui/styles/titles-cards.css +408 -408
  112. package/web-ui/styles.css +20 -18
  113. package/web-ui.html +17 -17
  114. package/README.en.md +0 -349
@@ -1,237 +1,234 @@
1
- <!-- Provider 配置模式(Codex) -->
2
- <div
3
- v-show="mainTab === 'config' && isProviderConfigMode"
4
- class="mode-content mode-cards"
5
- id="panel-config-provider"
6
- role="tabpanel"
7
- :aria-labelledby="'tab-config-' + configMode">
8
- <!-- 添加提供商按钮 -->
9
- <button class="btn-add" @click="showAddModal = true" v-if="!loading && !initError">
10
- <svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
11
- <path d="M10 4v12M4 10h12"/>
12
- </svg>
13
- 新增提供商
14
- </button>
15
-
16
- <!-- 模型选择器 -->
17
- <div class="selector-section">
18
- <div class="selector-header">
19
- <span class="selector-title">模型</span>
20
- <div class="selector-actions">
21
- <button class="btn-icon" @click="showModelModal = true" aria-label="Add model" title="添加模型" v-if="modelsSource === 'legacy'">+</button>
22
- <button class="btn-icon" @click="showModelListModal = true" aria-label="Manage models" title="管理模型" v-if="modelsSource === 'legacy'">≡</button>
23
- </div>
24
- </div>
25
- <select
26
- v-if="codexModelsLoading || modelsSource === 'remote'"
27
- class="model-select"
28
- v-model="currentModel"
29
- @change="onModelChange"
30
- :disabled="codexModelsLoading"
31
- >
32
- <option v-if="codexModelsLoading" value="">加载中...</option>
33
- <option v-else v-for="model in models" :key="model" :value="model">{{ model }}</option>
34
- </select>
35
- <input
36
- v-if="!codexModelsLoading && (modelsSource !== 'remote' || !modelsHasCurrent)"
37
- class="model-input"
38
- v-model="currentModel"
39
- @blur="onModelChange"
40
- :placeholder="activeProviderModelPlaceholder"
41
- >
42
- <div class="config-template-hint" v-if="modelsSource === 'unlimited'">
43
- 当前无模型列表,可手填。
44
- </div>
45
- <div class="config-template-hint" v-if="modelsSource === 'error'">
46
- 模型列表获取失败,可手填。
47
- </div>
48
- <div class="config-template-hint" v-if="modelsSource === 'remote' && !modelsHasCurrent">
49
- {{ isCodexConfigMode ? '当前模型不在列表,可手填或改模板。' : '当前模型不在列表,可手填。' }}
50
- </div>
51
- <div class="config-template-hint" v-if="isCodexConfigMode">
52
- 先改模板,再应用。
53
- </div>
54
- <div class="config-template-hint" v-else-if="activeProviderBridgeHint">
55
- {{ activeProviderBridgeHint }} 模板仅限 Codex 编辑。
56
- </div>
57
- <button class="btn-tool btn-template-editor" v-if="isCodexConfigMode" @click="openConfigTemplateEditor" :disabled="loading || !!initError">
58
- 打开模板编辑器
59
- </button>
60
- </div>
61
-
62
- <template v-if="isCodexConfigMode">
63
- <div class="selector-section">
64
- <div class="selector-header">
65
- <span class="selector-title">服务档</span>
66
- </div>
67
- <select class="model-select" v-model="serviceTier" @change="onServiceTierChange">
68
- <option value="fast">fast(默认)</option>
69
- <option value="standard">standard</option>
70
- </select>
71
- <div class="config-template-hint">
72
- fast 写入 <code>service_tier</code>。
73
- </div>
74
- </div>
75
-
76
- <div class="selector-section">
77
- <div class="selector-header">
78
- <span class="selector-title">推理强度</span>
79
- </div>
80
- <select class="model-select" v-model="modelReasoningEffort" @change="onReasoningEffortChange">
81
- <option value="high">high</option>
82
- <option value="medium">medium(默认)</option>
83
- <option value="low">low</option>
84
- <option value="xhigh">xhigh</option>
85
- </select>
86
- <div class="config-template-hint">
87
- 控制推理深度;high 更深。
88
- </div>
89
- </div>
90
-
91
- <div class="selector-section">
92
- <div class="selector-header">
93
- <span class="selector-title">压缩阈值</span>
94
- <div class="selector-actions">
95
- <button
96
- class="btn-tool btn-tool-compact"
97
- @click="resetCodexContextBudgetDefaults"
98
- :disabled="loading || !!initError || codexApplying">
99
- 重置
100
- </button>
101
- </div>
102
- </div>
103
- <div class="codex-config-grid">
104
- <div class="form-group codex-config-field">
105
- <label class="form-label" for="codex-model-context-window">model_context_window</label>
106
- <input
107
- id="codex-model-context-window"
108
- v-model="modelContextWindowInput"
109
- class="form-input"
110
- inputmode="numeric"
111
- autocomplete="off"
112
- placeholder="例如: 190000"
113
- @focus="editingCodexBudgetField = 'modelContextWindowInput'"
114
- @input="sanitizePositiveIntegerDraft('modelContextWindowInput')"
115
- @blur="onModelContextWindowBlur"
116
- @keydown.enter.prevent="onModelContextWindowBlur">
117
- <div class="form-hint">上下文上限,默认 190000。</div>
118
- </div>
119
- <div class="form-group codex-config-field">
120
- <label class="form-label" for="codex-model-auto-compact-token-limit">model_auto_compact_token_limit</label>
121
- <input
122
- id="codex-model-auto-compact-token-limit"
123
- v-model="modelAutoCompactTokenLimitInput"
124
- class="form-input"
125
- inputmode="numeric"
126
- autocomplete="off"
127
- placeholder="例如: 185000"
128
- @focus="editingCodexBudgetField = 'modelAutoCompactTokenLimitInput'"
129
- @input="sanitizePositiveIntegerDraft('modelAutoCompactTokenLimitInput')"
130
- @blur="onModelAutoCompactTokenLimitBlur"
131
- @keydown.enter.prevent="onModelAutoCompactTokenLimitBlur">
132
- <div class="form-hint">自动压缩阈值,默认 185000。</div>
133
- </div>
134
- </div>
135
- </div>
136
-
137
- <div class="selector-section">
138
- <div class="selector-header">
139
- <span class="selector-title">AGENTS.md</span>
140
- </div>
141
- <button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">
142
- {{ agentsLoading ? '加载中...' : '打开 AGENTS.md' }}
143
- </button>
144
- </div>
145
-
146
- </template>
147
-
148
- <div class="selector-section">
149
- <div class="selector-header">
150
- <span class="selector-title">健康检测</span>
151
- </div>
152
- <button class="btn-tool" @click="openHealthCheckDialog()" :disabled="loading || !!initError">
153
- 检测对话
154
- </button>
155
- </div>
156
-
157
- <div v-if="!loading && !initError" class="card-list">
158
- <div v-for="provider in displayProvidersList" :key="provider.name"
159
- :class="['card', { active: displayCurrentProvider === provider.name }]"
160
- @click="switchProvider(provider.name)"
161
- @keydown.enter.self.prevent="switchProvider(provider.name)"
162
- @keydown.space.self.prevent="switchProvider(provider.name)"
163
- tabindex="0"
164
- role="button"
165
- :aria-current="displayCurrentProvider === provider.name ? 'true' : null">
166
- <div class="card-leading">
167
- <div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}</div>
168
- <div class="card-content">
169
- <div class="card-title">
170
- <span>{{ provider.name }}</span>
171
- <span v-if="provider.readOnly" class="provider-readonly-badge">系统</span>
172
- </div>
173
- <div class="card-subtitle">
174
- {{ provider.url || '未设 URL' }}
175
- </div>
176
- </div>
177
- </div>
178
- <div class="card-trailing">
179
- <span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">
180
- {{ providerPillText(provider) }}
181
- </span>
182
- <span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">
183
- {{ formatLatency(speedResults[provider.name]) }}
184
- </span>
185
- <div class="card-actions" @click.stop>
186
- <button class="card-action-btn" @click="openHealthCheckDialog({ providerName: provider.name, locked: true })" :aria-label="`Open health dialog for ${provider.name}`" title="检测对话">
187
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
188
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
189
- <path d="M8 9h8"/>
190
- <path d="M8 13h5"/>
191
- </svg>
192
- </button>
193
- <button
194
- v-if="!provider.readOnly"
195
- class="card-action-btn"
196
- :class="{ loading: providerShareLoading[provider.name], disabled: !shouldAllowProviderShare(provider) }"
197
- :disabled="providerShareLoading[provider.name] || !shouldAllowProviderShare(provider)"
198
- @click="copyProviderShareCommand(provider)"
199
- :title="shouldAllowProviderShare(provider) ? '分享命令' : '不可分享'"
200
- aria-label="Share import command">
201
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
202
- <path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
203
- <path d="M16 6l-4-4-4 4"/>
204
- <path d="M12 2v14"/>
205
- </svg>
206
- </button>
207
- <button
208
- v-if="!provider.readOnly"
209
- class="card-action-btn"
210
- :class="{ disabled: !shouldShowProviderEdit(provider) }"
211
- :disabled="!shouldShowProviderEdit(provider)"
212
- @click="openEditModal(provider)"
213
- :aria-label="`Edit provider ${provider.name}`"
214
- :title="shouldShowProviderEdit(provider) ? '编辑' : '不可编辑'">
215
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
216
- <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
217
- <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
218
- </svg>
219
- </button>
220
- <button
221
- v-if="!provider.readOnly"
222
- class="card-action-btn delete"
223
- :class="{ disabled: !shouldShowProviderDelete(provider) }"
224
- :disabled="!shouldShowProviderDelete(provider)"
225
- @click="deleteProvider(provider.name)"
226
- :aria-label="`Delete provider ${provider.name}`"
227
- :title="shouldShowProviderDelete(provider) ? '删除' : '不可删除'">
228
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
229
- <path d="M3 6h18"/>
230
- <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
231
- </svg>
232
- </button>
233
- </div>
234
- </div>
235
- </div>
236
- </div>
237
- </div>
1
+ <!-- Provider 配置模式(Codex) -->
2
+ <div
3
+ v-show="mainTab === 'config' && isProviderConfigMode"
4
+ class="mode-content mode-cards"
5
+ id="panel-config-provider"
6
+ role="tabpanel"
7
+ :aria-labelledby="'tab-config-' + configMode">
8
+ <!-- 添加提供商按钮 -->
9
+ <button class="btn-add" @click="showAddModal = true" v-if="!loading && !initError">
10
+ <svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
11
+ <path d="M10 4v12M4 10h12"/>
12
+ </svg>
13
+ {{ t('config.addProvider') }}
14
+ </button>
15
+
16
+ <!-- 模型选择器 -->
17
+ <div class="selector-section">
18
+ <div class="selector-header">
19
+ <span class="selector-title">{{ t('config.models') }}</span>
20
+ <div class="selector-actions">
21
+ <button class="btn-icon" @click="showModelModal = true" aria-label="Add model" title="添加模型" v-if="modelsSource === 'legacy'">+</button>
22
+ <button class="btn-icon" @click="showModelListModal = true" aria-label="Manage models" title="管理模型" v-if="modelsSource === 'legacy'">≡</button>
23
+ </div>
24
+ </div>
25
+ <select
26
+ v-if="codexModelsLoading || modelsSource === 'remote'"
27
+ class="model-select"
28
+ v-model="currentModel"
29
+ @change="onModelChange"
30
+ :disabled="codexModelsLoading"
31
+ >
32
+ <option v-if="codexModelsLoading" value="">{{ t('config.modelLoading') }}</option>
33
+ <option v-else v-for="model in models" :key="model" :value="model">{{ model }}</option>
34
+ </select>
35
+ <input
36
+ v-if="!codexModelsLoading && (modelsSource !== 'remote' || !modelsHasCurrent)"
37
+ class="model-input"
38
+ v-model="currentModel"
39
+ @blur="onModelChange"
40
+ :placeholder="activeProviderModelPlaceholder"
41
+ >
42
+ <div class="config-template-hint" v-if="modelsSource === 'unlimited'">
43
+ {{ t('config.models.unlimited') }}
44
+ </div>
45
+ <div class="config-template-hint" v-if="modelsSource === 'error'">
46
+ {{ t('config.models.error') }}
47
+ </div>
48
+ <div class="config-template-hint" v-if="modelsSource === 'remote' && !modelsHasCurrent">
49
+ {{ isCodexConfigMode ? t('config.models.notInList.codex') : t('config.models.notInList.other') }}
50
+ </div>
51
+ <div class="config-template-hint" v-if="isCodexConfigMode">
52
+ {{ t('config.template.editFirst') }}
53
+ </div>
54
+ <div class="config-template-hint" v-else-if="activeProviderBridgeHint">
55
+ {{ t('config.template.bridgeCodexOnly', { hint: activeProviderBridgeHint }) }}
56
+ </div>
57
+ <button class="btn-tool btn-template-editor" v-if="isCodexConfigMode" @click="openConfigTemplateEditor" :disabled="loading || !!initError">
58
+ {{ t('config.template.openEditor') }}
59
+ </button>
60
+ </div>
61
+
62
+ <template v-if="isCodexConfigMode">
63
+ <div class="selector-section">
64
+ <div class="selector-header">
65
+ <span class="selector-title">{{ t('config.serviceTier') }}</span>
66
+ </div>
67
+ <select class="model-select" v-model="serviceTier" @change="onServiceTierChange">
68
+ <option value="fast">{{ t('config.serviceTier.fast') }}</option>
69
+ <option value="standard">standard</option>
70
+ </select>
71
+ <div class="config-template-hint">
72
+ {{ t('config.serviceTier.hint', { field: 'service_tier' }) }}
73
+ </div>
74
+ </div>
75
+
76
+ <div class="selector-section">
77
+ <div class="selector-header">
78
+ <span class="selector-title">{{ t('config.reasoningEffort') }}</span>
79
+ </div>
80
+ <select class="model-select" v-model="modelReasoningEffort" @change="onReasoningEffortChange">
81
+ <option value="high">high</option>
82
+ <option value="medium">{{ t('config.reasoningEffort.medium') }}</option>
83
+ <option value="low">low</option>
84
+ <option value="xhigh">xhigh</option>
85
+ </select>
86
+ <div class="config-template-hint">
87
+ {{ t('config.reasoningEffort.hint') }}
88
+ </div>
89
+ </div>
90
+
91
+ <div class="selector-section">
92
+ <div class="selector-header">
93
+ <span class="selector-title">{{ t('config.contextBudget') }}</span>
94
+ <div class="selector-actions">
95
+ <button
96
+ class="btn-tool btn-tool-compact"
97
+ @click="resetCodexContextBudgetDefaults"
98
+ :disabled="loading || !!initError || codexApplying">
99
+ {{ t('config.reset') }}
100
+ </button>
101
+ </div>
102
+ </div>
103
+ <div class="codex-config-grid">
104
+ <div class="form-group codex-config-field">
105
+ <label class="form-label" for="codex-model-context-window">model_context_window</label>
106
+ <input
107
+ id="codex-model-context-window"
108
+ v-model="modelContextWindowInput"
109
+ class="form-input"
110
+ inputmode="numeric"
111
+ autocomplete="off"
112
+ :placeholder="t('config.example', { value: 190000 })"
113
+ @focus="editingCodexBudgetField = 'modelContextWindowInput'"
114
+ @input="sanitizePositiveIntegerDraft('modelContextWindowInput')"
115
+ @blur="onModelContextWindowBlur"
116
+ @keydown.enter.prevent="onModelContextWindowBlur">
117
+ <div class="form-hint">{{ t('config.contextWindow.hint') }}</div>
118
+ </div>
119
+ <div class="form-group codex-config-field">
120
+ <label class="form-label" for="codex-model-auto-compact-token-limit">model_auto_compact_token_limit</label>
121
+ <input
122
+ id="codex-model-auto-compact-token-limit"
123
+ v-model="modelAutoCompactTokenLimitInput"
124
+ class="form-input"
125
+ inputmode="numeric"
126
+ autocomplete="off"
127
+ :placeholder="t('config.example', { value: 185000 })"
128
+ @focus="editingCodexBudgetField = 'modelAutoCompactTokenLimitInput'"
129
+ @input="sanitizePositiveIntegerDraft('modelAutoCompactTokenLimitInput')"
130
+ @blur="onModelAutoCompactTokenLimitBlur"
131
+ @keydown.enter.prevent="onModelAutoCompactTokenLimitBlur">
132
+ <div class="form-hint">{{ t('config.autoCompact.hint') }}</div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ <div class="selector-section">
138
+ <div class="selector-header">
139
+ <span class="selector-title">AGENTS.md</span>
140
+ </div>
141
+ <button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">
142
+ {{ agentsLoading ? t('config.modelLoading') : t('config.agents.open') }}
143
+ </button>
144
+ </div>
145
+
146
+ </template>
147
+
148
+ <div v-if="!loading && !initError" class="card-list">
149
+ <div v-for="provider in displayProvidersList" :key="provider.name"
150
+ :class="['card', { active: displayCurrentProvider === provider.name }]"
151
+ @click="switchProvider(provider.name)"
152
+ @keydown.enter.self.prevent="switchProvider(provider.name)"
153
+ @keydown.space.self.prevent="switchProvider(provider.name)"
154
+ tabindex="0"
155
+ role="button"
156
+ :aria-current="displayCurrentProvider === provider.name ? 'true' : null">
157
+ <div class="card-leading">
158
+ <div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}</div>
159
+ <div class="card-content">
160
+ <div class="card-title">
161
+ <span>{{ provider.name }}</span>
162
+ <span v-if="provider.readOnly" class="provider-readonly-badge">{{ t('config.badge.system') }}</span>
163
+ </div>
164
+ <div class="card-subtitle">
165
+ {{ provider.url || t('config.url.unset') }}
166
+ </div>
167
+ </div>
168
+ </div>
169
+ <div class="card-trailing">
170
+ <span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">
171
+ {{ providerPillText(provider) }}
172
+ </span>
173
+ <span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">
174
+ {{ formatLatency(speedResults[provider.name]) }}
175
+ </span>
176
+ <div class="card-actions" @click.stop>
177
+ <button
178
+ class="card-action-btn"
179
+ @click="openHealthCheckDialog({ providerName: provider.name, locked: true })"
180
+ :disabled="displayCurrentProvider !== provider.name"
181
+ :aria-label="`Open health dialog for ${provider.name}`"
182
+ :title="displayCurrentProvider === provider.name ? t('config.healthTest') : t('config.switchProviderFirst')"
183
+ >
184
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
185
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
186
+ <path d="M8 9h8"/>
187
+ <path d="M8 13h5"/>
188
+ </svg>
189
+ </button>
190
+ <button
191
+ v-if="!provider.readOnly"
192
+ class="card-action-btn"
193
+ :class="{ loading: providerShareLoading[provider.name], disabled: !shouldAllowProviderShare(provider) }"
194
+ :disabled="providerShareLoading[provider.name] || !shouldAllowProviderShare(provider)"
195
+ @click="copyProviderShareCommand(provider)"
196
+ :title="shouldAllowProviderShare(provider) ? t('config.shareCommand') : t('config.shareDisabled')"
197
+ aria-label="Share import command">
198
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
199
+ <path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
200
+ <path d="M16 6l-4-4-4 4"/>
201
+ <path d="M12 2v14"/>
202
+ </svg>
203
+ </button>
204
+ <button
205
+ v-if="!provider.readOnly"
206
+ class="card-action-btn"
207
+ :class="{ disabled: !shouldShowProviderEdit(provider) }"
208
+ :disabled="!shouldShowProviderEdit(provider)"
209
+ @click="openEditModal(provider)"
210
+ :aria-label="`Edit provider ${provider.name}`"
211
+ :title="shouldShowProviderEdit(provider) ? '编辑' : '不可编辑'">
212
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
213
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
214
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
215
+ </svg>
216
+ </button>
217
+ <button
218
+ v-if="!provider.readOnly"
219
+ class="card-action-btn delete"
220
+ :class="{ disabled: !shouldShowProviderDelete(provider) }"
221
+ :disabled="!shouldShowProviderDelete(provider)"
222
+ @click="deleteProvider(provider.name)"
223
+ :aria-label="`Delete provider ${provider.name}`"
224
+ :title="shouldShowProviderDelete(provider) ? '删除' : '不可删除'">
225
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
226
+ <path d="M3 6h18"/>
227
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
228
+ </svg>
229
+ </button>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ </div>