codexmate 0.0.19 → 0.0.20
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/README.en.md +8 -4
- package/README.md +8 -4
- package/cli/config-health.js +338 -0
- package/cli.js +1136 -584
- package/lib/cli-models-utils.js +186 -27
- package/lib/cli-network-utils.js +117 -101
- package/package.json +8 -1
- package/web-ui/app.js +381 -5532
- package/web-ui/index.html +15 -2231
- package/web-ui/logic.agents-diff.mjs +386 -0
- package/web-ui/logic.claude.mjs +108 -0
- package/web-ui/logic.mjs +5 -793
- package/web-ui/logic.runtime.mjs +124 -0
- package/web-ui/logic.sessions.mjs +263 -0
- package/web-ui/modules/api.mjs +69 -0
- package/web-ui/modules/app.computed.dashboard.mjs +113 -0
- package/web-ui/modules/app.computed.index.mjs +13 -0
- package/web-ui/modules/app.computed.session.mjs +141 -0
- package/web-ui/modules/app.constants.mjs +15 -0
- package/web-ui/modules/app.methods.agents.mjs +493 -0
- package/web-ui/modules/app.methods.claude-config.mjs +174 -0
- package/web-ui/modules/app.methods.codex-config.mjs +640 -0
- package/web-ui/modules/app.methods.index.mjs +86 -0
- package/web-ui/modules/app.methods.install.mjs +157 -0
- package/web-ui/modules/app.methods.navigation.mjs +478 -0
- package/web-ui/modules/app.methods.openclaw-core.mjs +514 -0
- package/web-ui/modules/app.methods.openclaw-editing.mjs +337 -0
- package/web-ui/modules/app.methods.openclaw-persist.mjs +251 -0
- package/web-ui/modules/app.methods.providers.mjs +265 -0
- package/web-ui/modules/app.methods.runtime.mjs +323 -0
- package/web-ui/modules/app.methods.session-actions.mjs +457 -0
- package/web-ui/modules/app.methods.session-browser.mjs +435 -0
- package/web-ui/modules/app.methods.session-timeline.mjs +441 -0
- package/web-ui/modules/app.methods.session-trash.mjs +419 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +406 -0
- package/web-ui/partials/index/layout-footer.html +69 -0
- package/web-ui/partials/index/layout-header.html +337 -0
- package/web-ui/partials/index/modal-config-template-agents.html +125 -0
- package/web-ui/partials/index/modal-confirm-toast.html +32 -0
- package/web-ui/partials/index/modal-health-check.html +72 -0
- package/web-ui/partials/index/modal-openclaw-config.html +275 -0
- package/web-ui/partials/index/modal-skills.html +184 -0
- package/web-ui/partials/index/modals-basic.html +196 -0
- package/web-ui/partials/index/panel-config-claude.html +100 -0
- package/web-ui/partials/index/panel-config-codex.html +237 -0
- package/web-ui/partials/index/panel-config-openclaw.html +84 -0
- package/web-ui/partials/index/panel-market.html +174 -0
- package/web-ui/partials/index/panel-sessions.html +387 -0
- package/web-ui/partials/index/panel-settings.html +166 -0
- package/web-ui/source-bundle.cjs +233 -0
- package/web-ui/styles/base-theme.css +373 -0
- package/web-ui/styles/controls-forms.css +354 -0
- package/web-ui/styles/feedback.css +108 -0
- package/web-ui/styles/health-check-dialog.css +144 -0
- package/web-ui/styles/layout-shell.css +330 -0
- package/web-ui/styles/modals-core.css +449 -0
- package/web-ui/styles/navigation-panels.css +381 -0
- package/web-ui/styles/openclaw-structured.css +266 -0
- package/web-ui/styles/responsive.css +416 -0
- package/web-ui/styles/sessions-list.css +414 -0
- package/web-ui/styles/sessions-preview.css +405 -0
- package/web-ui/styles/sessions-toolbar-trash.css +243 -0
- package/web-ui/styles/sessions-usage.css +276 -0
- package/web-ui/styles/skills-list.css +298 -0
- package/web-ui/styles/skills-market.css +335 -0
- package/web-ui/styles/titles-cards.css +407 -0
- package/web-ui/styles.css +16 -4668
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<!-- 添加提供商模态框 -->
|
|
2
|
+
<div v-if="showAddModal" class="modal-overlay" @click.self="closeAddModal">
|
|
3
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="add-provider-modal-title">
|
|
4
|
+
<div class="modal-title" id="add-provider-modal-title">添加提供商</div>
|
|
5
|
+
|
|
6
|
+
<div class="form-group">
|
|
7
|
+
<label class="form-label">名称</label>
|
|
8
|
+
<input v-model="newProvider.name" class="form-input" placeholder="例如: myapi">
|
|
9
|
+
</div>
|
|
10
|
+
<div class="form-group">
|
|
11
|
+
<label class="form-label">API 端点</label>
|
|
12
|
+
<input v-model="newProvider.url" class="form-input" placeholder="https://api.example.com/v1">
|
|
13
|
+
</div>
|
|
14
|
+
<div class="form-group">
|
|
15
|
+
<label class="form-label">认证密钥</label>
|
|
16
|
+
<input v-model="newProvider.key" class="form-input" type="password" placeholder="sk-...">
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="btn-group">
|
|
20
|
+
<button class="btn btn-cancel" @click="closeAddModal">取消</button>
|
|
21
|
+
<button class="btn btn-confirm" @click="addProvider">添加</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div v-if="showInstallModal" class="modal-overlay" @click.self="closeInstallModal">
|
|
27
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="install-cli-modal-title">
|
|
28
|
+
<div class="modal-header">
|
|
29
|
+
<div class="modal-title" id="install-cli-modal-title">CLI 安装</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="list-row">
|
|
33
|
+
<label class="form-label">包管理器</label>
|
|
34
|
+
<select class="form-input" v-model="installPackageManager">
|
|
35
|
+
<option value="npm">npm</option>
|
|
36
|
+
<option value="pnpm">pnpm</option>
|
|
37
|
+
<option value="bun">bun</option>
|
|
38
|
+
</select>
|
|
39
|
+
<label class="form-label">镜像源</label>
|
|
40
|
+
<div class="install-action-tabs">
|
|
41
|
+
<button class="btn-mini" :class="{ active: installRegistryPreset === 'default' }" @click="setInstallRegistryPreset('default')">官方</button>
|
|
42
|
+
<button class="btn-mini" :class="{ active: installRegistryPreset === 'npmmirror' }" @click="setInstallRegistryPreset('npmmirror')">npmmirror</button>
|
|
43
|
+
<button class="btn-mini" :class="{ active: installRegistryPreset === 'tencent' }" @click="setInstallRegistryPreset('tencent')">腾讯云</button>
|
|
44
|
+
<button class="btn-mini" :class="{ active: installRegistryPreset === 'custom' }" @click="setInstallRegistryPreset('custom')">自定义</button>
|
|
45
|
+
</div>
|
|
46
|
+
<input
|
|
47
|
+
v-if="installRegistryPreset === 'custom'"
|
|
48
|
+
v-model="installRegistryCustom"
|
|
49
|
+
class="form-input install-registry-input"
|
|
50
|
+
placeholder="https://registry.example.com">
|
|
51
|
+
<div class="form-hint install-registry-hint" v-if="installRegistryPreview">
|
|
52
|
+
命令将附加:--registry={{ installRegistryPreview }}
|
|
53
|
+
</div>
|
|
54
|
+
<div class="form-hint install-registry-hint" v-else-if="installRegistryPreset === 'custom'">
|
|
55
|
+
请输入完整 URL(含 http/https),将自动附加到命令。
|
|
56
|
+
</div>
|
|
57
|
+
<label class="form-label">操作</label>
|
|
58
|
+
<div class="install-action-tabs">
|
|
59
|
+
<button class="btn-mini" :class="{ active: installCommandAction === 'install' }" @click="setInstallCommandAction('install')">安装</button>
|
|
60
|
+
<button class="btn-mini" :class="{ active: installCommandAction === 'update' }" @click="setInstallCommandAction('update')">升级</button>
|
|
61
|
+
<button class="btn-mini" :class="{ active: installCommandAction === 'uninstall' }" @click="setInstallCommandAction('uninstall')">卸载</button>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="install-list">
|
|
66
|
+
<div
|
|
67
|
+
class="install-row"
|
|
68
|
+
v-for="target in installTargetCards"
|
|
69
|
+
:key="'install-command-' + target.id + '-' + installCommandAction">
|
|
70
|
+
<div class="install-row-main">
|
|
71
|
+
<div class="install-row-title">{{ target.name }}</div>
|
|
72
|
+
<code class="install-command">{{ target.command }}</code>
|
|
73
|
+
</div>
|
|
74
|
+
<button class="btn-mini" :disabled="!target.command" @click="copyInstallCommand(target.command)">复制</button>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="install-help">
|
|
79
|
+
<div class="form-label">常见问题</div>
|
|
80
|
+
<ul class="install-help-list">
|
|
81
|
+
<li v-for="tip in installTroubleshootingTips" :key="tip">{{ tip }}</li>
|
|
82
|
+
</ul>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<!-- 编辑提供商模态框 -->
|
|
88
|
+
<div v-if="showEditModal" class="modal-overlay" @click.self="closeEditModal">
|
|
89
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="edit-provider-modal-title">
|
|
90
|
+
<div class="modal-title" id="edit-provider-modal-title">编辑提供商</div>
|
|
91
|
+
|
|
92
|
+
<div class="form-group">
|
|
93
|
+
<label class="form-label">名称</label>
|
|
94
|
+
<input v-model="editingProvider.name" class="form-input" placeholder="提供商名称" readonly>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="form-group">
|
|
97
|
+
<label class="form-label">API 端点</label>
|
|
98
|
+
<input v-model="editingProvider.url" class="form-input" placeholder="https://api.example.com/v1">
|
|
99
|
+
</div>
|
|
100
|
+
<div class="form-group">
|
|
101
|
+
<label class="form-label">认证密钥</label>
|
|
102
|
+
<input v-model="editingProvider.key" class="form-input" type="password" placeholder="留空则保持不变">
|
|
103
|
+
<div class="form-hint">留空表示不修改密钥</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="btn-group">
|
|
107
|
+
<button class="btn btn-cancel" @click="closeEditModal">取消</button>
|
|
108
|
+
<button class="btn btn-confirm" @click="updateProvider">保存</button>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<!-- 添加模型模态框 -->
|
|
114
|
+
<div v-if="showModelModal" class="modal-overlay" @click.self="closeModelModal">
|
|
115
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="add-model-modal-title">
|
|
116
|
+
<div class="modal-title" id="add-model-modal-title">添加模型</div>
|
|
117
|
+
|
|
118
|
+
<div class="form-group">
|
|
119
|
+
<label class="form-label">模型名称</label>
|
|
120
|
+
<input v-model="newModelName" class="form-input" placeholder="例如: gpt-5">
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="btn-group">
|
|
124
|
+
<button class="btn btn-cancel" @click="closeModelModal">取消</button>
|
|
125
|
+
<button class="btn btn-confirm" @click="addModel">添加</button>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<!-- 模型列表模态框 -->
|
|
131
|
+
<div v-if="showModelListModal" class="modal-overlay" @click.self="showModelListModal = false">
|
|
132
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="manage-models-modal-title">
|
|
133
|
+
<div class="modal-title" id="manage-models-modal-title">管理模型</div>
|
|
134
|
+
|
|
135
|
+
<div class="model-list">
|
|
136
|
+
<div v-for="model in models" :key="model" class="model-item">
|
|
137
|
+
<span>{{ model }}</span>
|
|
138
|
+
<button type="button" class="btn-remove-model" @click="removeModel(model)">删除</button>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div class="btn-group">
|
|
143
|
+
<button class="btn btn-confirm" @click="showModelListModal = false">关闭</button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<!-- 添加Claude配置模态框 -->
|
|
149
|
+
<div v-if="showClaudeConfigModal" class="modal-overlay" @click.self="closeClaudeConfigModal">
|
|
150
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="add-claude-config-modal-title">
|
|
151
|
+
<div class="modal-title" id="add-claude-config-modal-title">添加 Claude Code 配置</div>
|
|
152
|
+
|
|
153
|
+
<div class="form-group">
|
|
154
|
+
<label class="form-label">配置名称</label>
|
|
155
|
+
<input v-model="newClaudeConfig.name" class="form-input" placeholder="例如: 智谱GLM">
|
|
156
|
+
</div>
|
|
157
|
+
<div class="form-group">
|
|
158
|
+
<label class="form-label">API Key</label>
|
|
159
|
+
<input v-model="newClaudeConfig.apiKey" class="form-input" type="password" autocomplete="off" spellcheck="false" placeholder="sk-ant-...">
|
|
160
|
+
</div>
|
|
161
|
+
<div class="form-group">
|
|
162
|
+
<label class="form-label">Base URL</label>
|
|
163
|
+
<input v-model="newClaudeConfig.baseUrl" class="form-input" placeholder="https://open.bigmodel.cn/api/anthropic">
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<div class="btn-group">
|
|
167
|
+
<button class="btn btn-cancel" @click="closeClaudeConfigModal">取消</button>
|
|
168
|
+
<button class="btn btn-confirm" @click="addClaudeConfig">添加</button>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- 编辑Claude配置模态框 -->
|
|
174
|
+
<div v-if="showEditConfigModal" class="modal-overlay" @click.self="closeEditConfigModal">
|
|
175
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="edit-claude-config-modal-title">
|
|
176
|
+
<div class="modal-title" id="edit-claude-config-modal-title">编辑 Claude Code 配置</div>
|
|
177
|
+
|
|
178
|
+
<div class="form-group">
|
|
179
|
+
<label class="form-label">配置名称</label>
|
|
180
|
+
<input v-model="editingConfig.name" class="form-input" placeholder="配置名称" readonly>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="form-group">
|
|
183
|
+
<label class="form-label">API Key</label>
|
|
184
|
+
<input v-model="editingConfig.apiKey" class="form-input" type="password" autocomplete="off" spellcheck="false" placeholder="sk-ant-...">
|
|
185
|
+
</div>
|
|
186
|
+
<div class="form-group">
|
|
187
|
+
<label class="form-label">Base URL</label>
|
|
188
|
+
<input v-model="editingConfig.baseUrl" class="form-input" placeholder="https://open.bigmodel.cn/api/anthropic">
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="btn-group">
|
|
192
|
+
<button class="btn btn-cancel" @click="closeEditConfigModal">取消</button>
|
|
193
|
+
<button class="btn btn-confirm" @click="saveAndApplyConfig">保存并应用</button>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<!-- Claude Code 配置模式 -->
|
|
2
|
+
<div
|
|
3
|
+
v-show="mainTab === 'config' && configMode === 'claude'"
|
|
4
|
+
class="mode-content mode-cards"
|
|
5
|
+
id="panel-config-claude"
|
|
6
|
+
role="tabpanel"
|
|
7
|
+
:aria-labelledby="'tab-config-claude'">
|
|
8
|
+
<!-- 添加提供商按钮 -->
|
|
9
|
+
<button class="btn-add" @click="openClaudeConfigModal" 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
|
+
<div class="config-template-hint">
|
|
16
|
+
默认应用到 <code>~/.claude/settings.json</code>。
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="selector-section">
|
|
20
|
+
<div class="selector-header">
|
|
21
|
+
<span class="selector-title">模型</span>
|
|
22
|
+
</div>
|
|
23
|
+
<select
|
|
24
|
+
v-if="claudeModelHasList"
|
|
25
|
+
class="model-select"
|
|
26
|
+
v-model="currentClaudeModel"
|
|
27
|
+
@change="onClaudeModelChange"
|
|
28
|
+
>
|
|
29
|
+
<option v-for="model in claudeModelOptions" :key="model" :value="model">{{ model }}</option>
|
|
30
|
+
</select>
|
|
31
|
+
<input
|
|
32
|
+
v-else
|
|
33
|
+
class="model-input"
|
|
34
|
+
v-model="currentClaudeModel"
|
|
35
|
+
@blur="onClaudeModelChange"
|
|
36
|
+
@keyup.enter="onClaudeModelChange"
|
|
37
|
+
placeholder="例如: claude-3-7-sonnet"
|
|
38
|
+
>
|
|
39
|
+
<div class="config-template-hint">
|
|
40
|
+
模型修改后会自动保存并应用到当前配置。
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="selector-section">
|
|
45
|
+
<div class="selector-header">
|
|
46
|
+
<span class="selector-title">配置健康检查</span>
|
|
47
|
+
</div>
|
|
48
|
+
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
49
|
+
{{ healthCheckLoading ? '检查中...' : '运行检查' }}
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="card-list">
|
|
54
|
+
<div v-for="(config, name) in claudeConfigs" :key="name"
|
|
55
|
+
:class="['card', { active: currentClaudeConfig === name }]"
|
|
56
|
+
@click="applyClaudeConfig(name)"
|
|
57
|
+
@keydown.enter.self.prevent="applyClaudeConfig(name)"
|
|
58
|
+
@keydown.space.self.prevent="applyClaudeConfig(name)"
|
|
59
|
+
tabindex="0"
|
|
60
|
+
role="button"
|
|
61
|
+
:aria-current="currentClaudeConfig === name ? 'true' : null">
|
|
62
|
+
<div class="card-leading">
|
|
63
|
+
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
|
|
64
|
+
<div class="card-content">
|
|
65
|
+
<div class="card-title">{{ name }}</div>
|
|
66
|
+
<div class="card-subtitle">{{ config.model || '未设置模型' }}</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="card-trailing">
|
|
70
|
+
<span :class="['pill', config.hasKey ? 'configured' : 'empty']">
|
|
71
|
+
{{ config.hasKey ? '已配置' : '未配置' }}
|
|
72
|
+
</span>
|
|
73
|
+
<span v-if="claudeSpeedResults[name]" :class="['latency', claudeSpeedResults[name].ok ? 'ok' : 'error']">
|
|
74
|
+
{{ formatLatency(claudeSpeedResults[name]) }}
|
|
75
|
+
</span>
|
|
76
|
+
<div class="card-actions" @click.stop>
|
|
77
|
+
<button class="card-action-btn" @click="openEditConfigModal(name)" :aria-label="`Edit Claude config ${name}`" title="编辑">
|
|
78
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
79
|
+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
80
|
+
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
81
|
+
</svg>
|
|
82
|
+
</button>
|
|
83
|
+
<button class="card-action-btn" :class="{ loading: claudeShareLoading[name] }" @click="copyClaudeShareCommand(name)" disabled title="分享导入命令(暂时禁用)" aria-label="Share import command">
|
|
84
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
85
|
+
<path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
|
|
86
|
+
<path d="M16 6l-4-4-4 4"/>
|
|
87
|
+
<path d="M12 2v14"/>
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
<button class="card-action-btn delete" @click="deleteClaudeConfig(name)" :aria-label="`Delete Claude config ${name}`" title="删除">
|
|
91
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
92
|
+
<path d="M3 6h18"/>
|
|
93
|
+
<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"/>
|
|
94
|
+
</svg>
|
|
95
|
+
</button>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
@@ -0,0 +1,237 @@
|
|
|
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] }"
|
|
197
|
+
disabled
|
|
198
|
+
@click="copyProviderShareCommand(provider)"
|
|
199
|
+
title="分享命令(暂不可用)"
|
|
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>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!-- OpenClaw 配置模式 -->
|
|
2
|
+
<div
|
|
3
|
+
v-show="mainTab === 'config' && configMode === 'openclaw'"
|
|
4
|
+
class="mode-content mode-cards"
|
|
5
|
+
id="panel-config-openclaw"
|
|
6
|
+
role="tabpanel"
|
|
7
|
+
:aria-labelledby="'tab-config-openclaw'">
|
|
8
|
+
<button class="btn-add" @click="openOpenclawAddModal" v-if="!loading && !initError">
|
|
9
|
+
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
10
|
+
<path d="M10 4v12M4 10h12"/>
|
|
11
|
+
</svg>
|
|
12
|
+
新增 OpenClaw 配置
|
|
13
|
+
</button>
|
|
14
|
+
<div class="config-template-hint">
|
|
15
|
+
写入 <code>~/.openclaw/openclaw.json</code>,支持 JSON5。
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="selector-section">
|
|
19
|
+
<div class="selector-header">
|
|
20
|
+
<span class="selector-title">AGENTS.md</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="config-template-hint">
|
|
23
|
+
读写 Workspace 的 <code>AGENTS.md</code>,默认路径 <code>~/.openclaw/workspace/AGENTS.md</code>。
|
|
24
|
+
</div>
|
|
25
|
+
<button class="btn-tool" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
26
|
+
{{ agentsLoading ? '加载中...' : '打开 AGENTS.md' }}
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="selector-section">
|
|
31
|
+
<div class="selector-header">
|
|
32
|
+
<label class="selector-title" for="openclaw-workspace-file">工作区文件</label>
|
|
33
|
+
</div>
|
|
34
|
+
<input
|
|
35
|
+
id="openclaw-workspace-file"
|
|
36
|
+
class="form-input"
|
|
37
|
+
v-model="openclawWorkspaceFileName"
|
|
38
|
+
placeholder="例如: SOUL.md">
|
|
39
|
+
<div class="config-template-hint">
|
|
40
|
+
仅限 Workspace 内的 <code>.md</code> 文件。
|
|
41
|
+
</div>
|
|
42
|
+
<button class="btn-tool" @click="openOpenclawWorkspaceEditor" :disabled="loading || !!initError || agentsLoading">
|
|
43
|
+
{{ agentsLoading ? '加载中...' : '打开工作区文件' }}
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="card-list">
|
|
48
|
+
<div v-for="(config, name) in openclawConfigs" :key="name"
|
|
49
|
+
:class="['card', { active: currentOpenclawConfig === name }]"
|
|
50
|
+
@click="applyOpenclawConfig(name)"
|
|
51
|
+
@keydown.enter.self.prevent="applyOpenclawConfig(name)"
|
|
52
|
+
@keydown.space.self.prevent="applyOpenclawConfig(name)"
|
|
53
|
+
tabindex="0"
|
|
54
|
+
role="button"
|
|
55
|
+
:aria-current="currentOpenclawConfig === name ? 'true' : null">
|
|
56
|
+
<div class="card-leading">
|
|
57
|
+
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
|
|
58
|
+
<div class="card-content">
|
|
59
|
+
<div class="card-title">{{ name }}</div>
|
|
60
|
+
<div class="card-subtitle">{{ openclawSubtitle(config) }}</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="card-trailing">
|
|
64
|
+
<span :class="['pill', openclawHasContent(config) ? 'configured' : 'empty']">
|
|
65
|
+
{{ openclawHasContent(config) ? '已配置' : '未配置' }}
|
|
66
|
+
</span>
|
|
67
|
+
<div class="card-actions" @click.stop>
|
|
68
|
+
<button class="card-action-btn" @click="openOpenclawEditModal(name)" :aria-label="`Edit OpenClaw config ${name}`" title="编辑">
|
|
69
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
70
|
+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
71
|
+
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
72
|
+
</svg>
|
|
73
|
+
</button>
|
|
74
|
+
<button class="card-action-btn delete" @click="deleteOpenclawConfig(name)" :aria-label="`Delete OpenClaw config ${name}`" title="删除">
|
|
75
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
76
|
+
<path d="M3 6h18"/>
|
|
77
|
+
<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"/>
|
|
78
|
+
</svg>
|
|
79
|
+
</button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|