codexmate 0.0.12 → 0.0.14
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 +429 -0
- package/README.md +241 -203
- package/cli.js +10210 -0
- package/doc/CHANGELOG.md +14 -1
- package/doc/CHANGELOG.zh-CN.md +13 -0
- package/lib/cli-utils.js +16 -0
- package/lib/mcp-stdio.js +440 -0
- package/lib/workflow-engine.js +340 -0
- package/package.json +63 -53
- package/web-ui/app.js +1417 -14
- package/web-ui/index.html +585 -67
- package/web-ui/logic.mjs +147 -1
- package/web-ui/styles.css +1049 -66
- package/README.zh-CN.md +0 -397
- package/src/cli.js +0 -5464
- package/src/lib/cli-file-utils.js +0 -151
- package/src/lib/cli-models-utils.js +0 -152
- package/src/lib/cli-network-utils.js +0 -148
- package/src/lib/cli-session-utils.js +0 -121
- package/src/lib/cli-utils.js +0 -139
- package/src/res/json5.min.js +0 -1
- package/src/res/logo.png +0 -0
- package/src/res/screenshot.png +0 -0
- package/src/res/vue.global.js +0 -18552
- package/src/web-ui/app.js +0 -2970
- package/src/web-ui/index.html +0 -1310
- package/src/web-ui/logic.mjs +0 -157
- package/src/web-ui/styles.css +0 -2868
- /package/{src/web-ui.html → web-ui.html} +0 -0
package/src/web-ui/index.html
DELETED
|
@@ -1,1310 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Codex Mate</title>
|
|
7
|
-
<link rel="icon" type="image/png" href="/res/logo.png">
|
|
8
|
-
<link rel="apple-touch-icon" href="/res/logo.png">
|
|
9
|
-
<script src="/res/vue.global.js"></script>
|
|
10
|
-
<script src="/res/json5.min.js"></script>
|
|
11
|
-
<link rel="stylesheet" href="/web-ui/styles.css">
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<div id="app" class="container" v-cloak>
|
|
15
|
-
<button class="fab-install" @click="openInstallModal" aria-label="安装 CLI">
|
|
16
|
-
安装 CLI
|
|
17
|
-
</button>
|
|
18
|
-
|
|
19
|
-
<div class="hero" v-if="!sessionStandalone">
|
|
20
|
-
<img class="hero-logo" src="/res/logo.png" alt="Codex Mate logo">
|
|
21
|
-
<div class="hero-title">
|
|
22
|
-
Codex <span class="accent">Mate.</span>
|
|
23
|
-
</div>
|
|
24
|
-
<div class="hero-subtitle">
|
|
25
|
-
配置中枢:管理 Codex / Claude / OpenClaw / 会话
|
|
26
|
-
<span class="sr-only">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw / 会话。</span>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
|
|
30
|
-
<div v-if="!sessionStandalone" class="top-tabs" role="tablist" aria-label="主导航">
|
|
31
|
-
<button class="top-tab"
|
|
32
|
-
id="tab-config-codex"
|
|
33
|
-
role="tab"
|
|
34
|
-
:tabindex="mainTab === 'config' && configMode === 'codex' ? 0 : -1"
|
|
35
|
-
:aria-selected="mainTab === 'config' && configMode === 'codex'"
|
|
36
|
-
:aria-pressed="mainTab === 'config' && configMode === 'codex'"
|
|
37
|
-
aria-controls="panel-config-codex"
|
|
38
|
-
:class="{ active: mainTab === 'config' && configMode === 'codex' }"
|
|
39
|
-
@click="switchConfigMode('codex')">Codex 配置</button>
|
|
40
|
-
<button class="top-tab"
|
|
41
|
-
id="tab-config-claude"
|
|
42
|
-
role="tab"
|
|
43
|
-
:tabindex="mainTab === 'config' && configMode === 'claude' ? 0 : -1"
|
|
44
|
-
:aria-selected="mainTab === 'config' && configMode === 'claude'"
|
|
45
|
-
:aria-pressed="mainTab === 'config' && configMode === 'claude'"
|
|
46
|
-
aria-controls="panel-config-claude"
|
|
47
|
-
:class="{ active: mainTab === 'config' && configMode === 'claude' }"
|
|
48
|
-
@click="switchConfigMode('claude')">Claude Code 配置</button>
|
|
49
|
-
<button class="top-tab"
|
|
50
|
-
id="tab-config-openclaw"
|
|
51
|
-
role="tab"
|
|
52
|
-
:tabindex="mainTab === 'config' && configMode === 'openclaw' ? 0 : -1"
|
|
53
|
-
:aria-selected="mainTab === 'config' && configMode === 'openclaw'"
|
|
54
|
-
:aria-pressed="mainTab === 'config' && configMode === 'openclaw'"
|
|
55
|
-
aria-controls="panel-config-openclaw"
|
|
56
|
-
:class="{ active: mainTab === 'config' && configMode === 'openclaw' }"
|
|
57
|
-
@click="switchConfigMode('openclaw')">OpenClaw 配置</button>
|
|
58
|
-
<button class="top-tab"
|
|
59
|
-
id="tab-sessions"
|
|
60
|
-
role="tab"
|
|
61
|
-
:tabindex="mainTab === 'sessions' ? 0 : -1"
|
|
62
|
-
:aria-selected="mainTab === 'sessions'"
|
|
63
|
-
:aria-pressed="mainTab === 'sessions'"
|
|
64
|
-
aria-controls="panel-sessions"
|
|
65
|
-
:class="{ active: mainTab === 'sessions' }"
|
|
66
|
-
@click="switchMainTab('sessions')">会话浏览</button>
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
|
-
<div :class="['app-shell', { standalone: sessionStandalone }]">
|
|
70
|
-
<aside class="side-rail" v-if="!sessionStandalone">
|
|
71
|
-
<div class="brand-block">
|
|
72
|
-
<div class="brand-logo-wrap">
|
|
73
|
-
<img class="brand-logo" src="/res/logo.png" alt="Codex Mate logo">
|
|
74
|
-
</div>
|
|
75
|
-
<div class="brand-title">
|
|
76
|
-
Codex <span class="accent">Mate</span>
|
|
77
|
-
</div>
|
|
78
|
-
<div class="brand-subtitle">
|
|
79
|
-
配置 / 会话切换器
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
<div class="side-section" role="tablist" aria-label="配置管理">
|
|
84
|
-
<div class="side-section-title">配置管理</div>
|
|
85
|
-
<button
|
|
86
|
-
role="tab"
|
|
87
|
-
id="side-tab-config-codex"
|
|
88
|
-
aria-controls="panel-config-codex"
|
|
89
|
-
:tabindex="mainTab === 'config' && configMode === 'codex' ? 0 : -1"
|
|
90
|
-
:aria-selected="mainTab === 'config' && configMode === 'codex'"
|
|
91
|
-
:aria-pressed="mainTab === 'config' && configMode === 'codex'"
|
|
92
|
-
:class="['side-item', { active: mainTab === 'config' && configMode === 'codex' }]"
|
|
93
|
-
@click="switchConfigMode('codex')">
|
|
94
|
-
<div class="side-item-title">Codex 配置</div>
|
|
95
|
-
<div class="side-item-meta">
|
|
96
|
-
<span>提供商 / 模型</span>
|
|
97
|
-
<span v-if="currentProvider">当前 {{ currentProvider }}</span>
|
|
98
|
-
</div>
|
|
99
|
-
</button>
|
|
100
|
-
<button
|
|
101
|
-
role="tab"
|
|
102
|
-
id="side-tab-config-claude"
|
|
103
|
-
aria-controls="panel-config-claude"
|
|
104
|
-
:tabindex="mainTab === 'config' && configMode === 'claude' ? 0 : -1"
|
|
105
|
-
:aria-selected="mainTab === 'config' && configMode === 'claude'"
|
|
106
|
-
:aria-pressed="mainTab === 'config' && configMode === 'claude'"
|
|
107
|
-
:class="['side-item', { active: mainTab === 'config' && configMode === 'claude' }]"
|
|
108
|
-
@click="switchConfigMode('claude')">
|
|
109
|
-
<div class="side-item-title">Claude Code 配置</div>
|
|
110
|
-
<div class="side-item-meta">
|
|
111
|
-
<span>Base URL / Key</span>
|
|
112
|
-
<span v-if="currentClaudeConfig">当前 {{ currentClaudeConfig }}</span>
|
|
113
|
-
</div>
|
|
114
|
-
</button>
|
|
115
|
-
<button
|
|
116
|
-
role="tab"
|
|
117
|
-
id="side-tab-config-openclaw"
|
|
118
|
-
aria-controls="panel-config-openclaw"
|
|
119
|
-
:tabindex="mainTab === 'config' && configMode === 'openclaw' ? 0 : -1"
|
|
120
|
-
:aria-selected="mainTab === 'config' && configMode === 'openclaw'"
|
|
121
|
-
:aria-pressed="mainTab === 'config' && configMode === 'openclaw'"
|
|
122
|
-
:class="['side-item', { active: mainTab === 'config' && configMode === 'openclaw' }]"
|
|
123
|
-
@click="switchConfigMode('openclaw')">
|
|
124
|
-
<div class="side-item-title">OpenClaw 配置</div>
|
|
125
|
-
<div class="side-item-meta">
|
|
126
|
-
<span>JSON5 / Workspace</span>
|
|
127
|
-
<span v-if="currentOpenclawConfig">当前 {{ currentOpenclawConfig }}</span>
|
|
128
|
-
</div>
|
|
129
|
-
</button>
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<div class="side-section" role="tablist" aria-label="会话管理">
|
|
133
|
-
<div class="side-section-title">会话管理</div>
|
|
134
|
-
<button
|
|
135
|
-
role="tab"
|
|
136
|
-
id="side-tab-sessions"
|
|
137
|
-
aria-controls="panel-sessions"
|
|
138
|
-
:tabindex="mainTab === 'sessions' ? 0 : -1"
|
|
139
|
-
:aria-selected="mainTab === 'sessions'"
|
|
140
|
-
:aria-pressed="mainTab === 'sessions'"
|
|
141
|
-
:class="['side-item', { active: mainTab === 'sessions' }]"
|
|
142
|
-
@click="switchMainTab('sessions')">
|
|
143
|
-
<div class="side-item-title">会话浏览</div>
|
|
144
|
-
<div class="side-item-meta">
|
|
145
|
-
<span>快速预览 / 导出</span>
|
|
146
|
-
<span>来源:{{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'codex' ? 'Codex' : 'Claude') }}</span>
|
|
147
|
-
</div>
|
|
148
|
-
</button>
|
|
149
|
-
</div>
|
|
150
|
-
</aside>
|
|
151
|
-
<main class="main-panel">
|
|
152
|
-
<div class="panel-header" v-if="!sessionStandalone">
|
|
153
|
-
<h1 class="main-title">
|
|
154
|
-
{{ mainTab === 'config' ? '配置中心' : '会话浏览' }}
|
|
155
|
-
</h1>
|
|
156
|
-
<p class="subtitle" v-if="mainTab === 'config'">
|
|
157
|
-
配置中枢:管理 Codex / Claude / OpenClaw
|
|
158
|
-
<span class="sr-only">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw。</span>
|
|
159
|
-
</p>
|
|
160
|
-
<p class="subtitle" v-else>
|
|
161
|
-
浏览、导出或独立查看 Codex / Claude 会话记录。
|
|
162
|
-
</p>
|
|
163
|
-
</div>
|
|
164
|
-
|
|
165
|
-
<div class="status-strip" v-if="!sessionStandalone && mainTab === 'config'">
|
|
166
|
-
<template v-if="configMode === 'codex'">
|
|
167
|
-
<div class="status-chip">
|
|
168
|
-
<span class="label">Codex 提供商</span>
|
|
169
|
-
<span class="value">{{ currentProvider || '未选择' }}</span>
|
|
170
|
-
</div>
|
|
171
|
-
<div class="status-chip">
|
|
172
|
-
<span class="label">Codex 模型</span>
|
|
173
|
-
<span class="value">{{ currentModel || '未选择' }}</span>
|
|
174
|
-
</div>
|
|
175
|
-
</template>
|
|
176
|
-
<template v-else-if="configMode === 'claude'">
|
|
177
|
-
<div class="status-chip">
|
|
178
|
-
<span class="label">Claude 配置</span>
|
|
179
|
-
<span class="value">{{ currentClaudeConfig || '未选择' }}</span>
|
|
180
|
-
</div>
|
|
181
|
-
<div class="status-chip">
|
|
182
|
-
<span class="label">Claude 模型</span>
|
|
183
|
-
<span class="value">{{ currentClaudeModel || '未选择' }}</span>
|
|
184
|
-
</div>
|
|
185
|
-
</template>
|
|
186
|
-
<template v-else>
|
|
187
|
-
<div class="status-chip">
|
|
188
|
-
<span class="label">OpenClaw 配置</span>
|
|
189
|
-
<span class="value">{{ currentOpenclawConfig || '未选择' }}</span>
|
|
190
|
-
</div>
|
|
191
|
-
<div class="status-chip">
|
|
192
|
-
<span class="label">工作区文件</span>
|
|
193
|
-
<span class="value">{{ openclawWorkspaceFileName || '未选择' }}</span>
|
|
194
|
-
</div>
|
|
195
|
-
</template>
|
|
196
|
-
</div>
|
|
197
|
-
<div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'sessions'">
|
|
198
|
-
<div class="status-chip">
|
|
199
|
-
<span class="label">当前来源</span>
|
|
200
|
-
<span class="value">
|
|
201
|
-
{{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'claude' ? 'Claude Code' : 'Codex') }}
|
|
202
|
-
</span>
|
|
203
|
-
</div>
|
|
204
|
-
<div class="status-chip">
|
|
205
|
-
<span class="label">会话数</span>
|
|
206
|
-
<span class="value">{{ sessionsList.length }}</span>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
|
|
210
|
-
<div v-if="false && mainTab === 'config' && !sessionStandalone" class="config-subtabs">
|
|
211
|
-
<button :class="['config-subtab', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">
|
|
212
|
-
Codex 配置
|
|
213
|
-
</button>
|
|
214
|
-
<button :class="['config-subtab', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">
|
|
215
|
-
Claude Code 配置
|
|
216
|
-
</button>
|
|
217
|
-
<button :class="['config-subtab', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">
|
|
218
|
-
OpenClaw 配置
|
|
219
|
-
</button>
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
<!-- 内容包裹器 - 稳定布局 -->
|
|
223
|
-
<div class="content-wrapper">
|
|
224
|
-
<!-- Codex 配置模式 -->
|
|
225
|
-
<div
|
|
226
|
-
v-show="mainTab === 'config' && configMode === 'codex'"
|
|
227
|
-
class="mode-content mode-cards"
|
|
228
|
-
id="panel-config-codex"
|
|
229
|
-
role="tabpanel"
|
|
230
|
-
:aria-labelledby="'tab-config-codex'">
|
|
231
|
-
<!-- 添加提供商按钮 -->
|
|
232
|
-
<button class="btn-add" @click="showAddModal = true" v-if="!loading && !initError">
|
|
233
|
-
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
234
|
-
<path d="M10 4v12M4 10h12"/>
|
|
235
|
-
</svg>
|
|
236
|
-
添加提供商
|
|
237
|
-
</button>
|
|
238
|
-
|
|
239
|
-
<!-- 模型选择器 -->
|
|
240
|
-
<div class="selector-section">
|
|
241
|
-
<div class="selector-header">
|
|
242
|
-
<span class="selector-title">模型</span>
|
|
243
|
-
<div class="selector-actions">
|
|
244
|
-
<button class="btn-icon" @click="showModelModal = true" title="添加模型" v-if="modelsSource === 'legacy'">+</button>
|
|
245
|
-
<button class="btn-icon" @click="showModelListModal = true" title="管理模型" v-if="modelsSource === 'legacy'">≡</button>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
<select
|
|
249
|
-
v-if="codexModelsLoading || modelsSource === 'remote'"
|
|
250
|
-
class="model-select"
|
|
251
|
-
v-model="currentModel"
|
|
252
|
-
@change="onModelChange"
|
|
253
|
-
:disabled="codexModelsLoading"
|
|
254
|
-
>
|
|
255
|
-
<option v-if="codexModelsLoading" value="">加载中...</option>
|
|
256
|
-
<option v-else v-for="model in models" :key="model" :value="model">{{ model }}</option>
|
|
257
|
-
</select>
|
|
258
|
-
<input
|
|
259
|
-
v-if="!codexModelsLoading && (modelsSource !== 'remote' || !modelsHasCurrent)"
|
|
260
|
-
class="model-input"
|
|
261
|
-
v-model="currentModel"
|
|
262
|
-
@blur="onModelChange"
|
|
263
|
-
placeholder="例如: gpt-5.3-codex"
|
|
264
|
-
>
|
|
265
|
-
<div class="config-template-hint" v-if="modelsSource === 'unlimited'">
|
|
266
|
-
当前提供商未提供模型列表,视为不限。模型可手动输入。
|
|
267
|
-
</div>
|
|
268
|
-
<div class="config-template-hint" v-if="modelsSource === 'error'">
|
|
269
|
-
模型列表获取失败,请检查接口或手动输入。
|
|
270
|
-
</div>
|
|
271
|
-
<div class="config-template-hint" v-if="modelsSource === 'remote' && !modelsHasCurrent">
|
|
272
|
-
当前模型不在接口列表中,请手动输入或在模板中调整。
|
|
273
|
-
</div>
|
|
274
|
-
<div class="config-template-hint">
|
|
275
|
-
Codex 配置需先改模板,再手动应用。
|
|
276
|
-
</div>
|
|
277
|
-
<button class="btn-tool btn-template-editor" @click="openConfigTemplateEditor" :disabled="loading || !!initError">
|
|
278
|
-
打开 Config 模板编辑器
|
|
279
|
-
</button>
|
|
280
|
-
</div>
|
|
281
|
-
|
|
282
|
-
<div class="selector-section">
|
|
283
|
-
<div class="selector-header">
|
|
284
|
-
<span class="selector-title">服务档位</span>
|
|
285
|
-
</div>
|
|
286
|
-
<select class="model-select" v-model="serviceTier" @change="onServiceTierChange">
|
|
287
|
-
<option value="fast">fast(默认)</option>
|
|
288
|
-
<option value="standard">standard</option>
|
|
289
|
-
</select>
|
|
290
|
-
<div class="config-template-hint">
|
|
291
|
-
仅 fast 会写入 <code>service_tier</code>。
|
|
292
|
-
</div>
|
|
293
|
-
</div>
|
|
294
|
-
|
|
295
|
-
<div class="selector-section">
|
|
296
|
-
<div class="selector-header">
|
|
297
|
-
<span class="selector-title">推理努力</span>
|
|
298
|
-
</div>
|
|
299
|
-
<select class="model-select" v-model="modelReasoningEffort" @change="onReasoningEffortChange">
|
|
300
|
-
<option value="high">high(默认)</option>
|
|
301
|
-
<option value="medium">medium</option>
|
|
302
|
-
<option value="low">low</option>
|
|
303
|
-
<option value="xhigh">xhigh</option>
|
|
304
|
-
</select>
|
|
305
|
-
<div class="config-template-hint">
|
|
306
|
-
控制模型的推理深度,high 会更深入思考。
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
|
|
310
|
-
<div class="selector-section">
|
|
311
|
-
<div class="selector-header">
|
|
312
|
-
<span class="selector-title">AGENTS.md</span>
|
|
313
|
-
</div>
|
|
314
|
-
<button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
315
|
-
{{ agentsLoading ? '加载中...' : '打开 AGENTS.md 编辑器' }}
|
|
316
|
-
</button>
|
|
317
|
-
</div>
|
|
318
|
-
|
|
319
|
-
<div class="selector-section">
|
|
320
|
-
<div class="selector-header">
|
|
321
|
-
<span class="selector-title">配置健康检查</span>
|
|
322
|
-
</div>
|
|
323
|
-
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
324
|
-
{{ healthCheckLoading ? '检查中...' : '运行检查' }}
|
|
325
|
-
</button>
|
|
326
|
-
</div>
|
|
327
|
-
|
|
328
|
-
<div class="selector-section">
|
|
329
|
-
<div class="selector-header">
|
|
330
|
-
<span class="selector-title">配置维护</span>
|
|
331
|
-
</div>
|
|
332
|
-
<div class="config-template-hint">备份当前 config.toml 后写入默认配置。</div>
|
|
333
|
-
<button class="btn-tool" @click="resetConfig" :disabled="resetConfigLoading || loading || !!initError">
|
|
334
|
-
{{ resetConfigLoading ? '重装中...' : '一键重装配置' }}
|
|
335
|
-
</button>
|
|
336
|
-
</div>
|
|
337
|
-
|
|
338
|
-
<div v-if="!loading && !initError" class="card-list">
|
|
339
|
-
<div v-for="provider in providersList" :key="provider.name"
|
|
340
|
-
:class="['card', { active: currentProvider === provider.name }]"
|
|
341
|
-
@click="switchProvider(provider.name)">
|
|
342
|
-
<div class="card-leading">
|
|
343
|
-
<div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}</div>
|
|
344
|
-
<div class="card-content">
|
|
345
|
-
<div class="card-title">{{ provider.name }}</div>
|
|
346
|
-
<div class="card-subtitle">{{ provider.url || '未设置 URL' }}</div>
|
|
347
|
-
</div>
|
|
348
|
-
</div>
|
|
349
|
-
<div class="card-trailing">
|
|
350
|
-
<span :class="['pill', provider.hasKey ? 'configured' : 'empty']">
|
|
351
|
-
{{ provider.hasKey ? '已配置' : '未配置' }}
|
|
352
|
-
</span>
|
|
353
|
-
<span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">
|
|
354
|
-
{{ formatLatency(speedResults[provider.name]) }}
|
|
355
|
-
</span>
|
|
356
|
-
<div class="card-actions" @click.stop>
|
|
357
|
-
<button class="card-action-btn" :class="{ loading: speedLoading[provider.name] }" @click="runSpeedTest(provider.name)" title="Speed Test">
|
|
358
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
359
|
-
<path d="M13 2L3 14h7l-1 8 12-14h-7l-1-6z"/>
|
|
360
|
-
</svg>
|
|
361
|
-
</button>
|
|
362
|
-
<button class="card-action-btn" :class="{ loading: providerShareLoading[provider.name] }" @click="copyProviderShareCommand(provider)" title="分享导入命令">
|
|
363
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
364
|
-
<path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
|
|
365
|
-
<path d="M16 6l-4-4-4 4"/>
|
|
366
|
-
<path d="M12 2v14"/>
|
|
367
|
-
</svg>
|
|
368
|
-
</button>
|
|
369
|
-
<button class="card-action-btn" @click="openEditModal(provider)" title="编辑">
|
|
370
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
371
|
-
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
372
|
-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
373
|
-
</svg>
|
|
374
|
-
</button>
|
|
375
|
-
<button class="card-action-btn delete" @click="deleteProvider(provider.name)" title="删除">
|
|
376
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
377
|
-
<path d="M3 6h18"/>
|
|
378
|
-
<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"/>
|
|
379
|
-
</svg>
|
|
380
|
-
</button>
|
|
381
|
-
</div>
|
|
382
|
-
</div>
|
|
383
|
-
</div>
|
|
384
|
-
</div>
|
|
385
|
-
</div>
|
|
386
|
-
|
|
387
|
-
<!-- Claude Code 配置模式 -->
|
|
388
|
-
<div
|
|
389
|
-
v-show="mainTab === 'config' && configMode === 'claude'"
|
|
390
|
-
class="mode-content mode-cards"
|
|
391
|
-
id="panel-config-claude"
|
|
392
|
-
role="tabpanel"
|
|
393
|
-
:aria-labelledby="'tab-config-claude'">
|
|
394
|
-
<!-- 添加提供商按钮 -->
|
|
395
|
-
<button class="btn-add" @click="openClaudeConfigModal" v-if="!loading && !initError">
|
|
396
|
-
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
397
|
-
<path d="M10 4v12M4 10h12"/>
|
|
398
|
-
</svg>
|
|
399
|
-
添加提供商
|
|
400
|
-
</button>
|
|
401
|
-
<div class="config-template-hint">
|
|
402
|
-
默认应用到 <code>~/.claude/settings.json</code>。
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
<div class="selector-section">
|
|
406
|
-
<div class="selector-header">
|
|
407
|
-
<span class="selector-title">模型</span>
|
|
408
|
-
</div>
|
|
409
|
-
<select
|
|
410
|
-
v-if="claudeModelHasList"
|
|
411
|
-
class="model-select"
|
|
412
|
-
v-model="currentClaudeModel"
|
|
413
|
-
@change="onClaudeModelChange"
|
|
414
|
-
>
|
|
415
|
-
<option v-for="model in claudeModelOptions" :key="model" :value="model">{{ model }}</option>
|
|
416
|
-
</select>
|
|
417
|
-
<input
|
|
418
|
-
v-else
|
|
419
|
-
class="model-input"
|
|
420
|
-
v-model="currentClaudeModel"
|
|
421
|
-
@blur="onClaudeModelChange"
|
|
422
|
-
@keyup.enter="onClaudeModelChange"
|
|
423
|
-
placeholder="例如: claude-3-7-sonnet"
|
|
424
|
-
>
|
|
425
|
-
<div class="config-template-hint">
|
|
426
|
-
模型修改后会自动保存并应用到当前配置。
|
|
427
|
-
</div>
|
|
428
|
-
</div>
|
|
429
|
-
|
|
430
|
-
<div class="selector-section">
|
|
431
|
-
<div class="selector-header">
|
|
432
|
-
<span class="selector-title">配置健康检查</span>
|
|
433
|
-
</div>
|
|
434
|
-
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
435
|
-
{{ healthCheckLoading ? '检查中...' : '运行检查' }}
|
|
436
|
-
</button>
|
|
437
|
-
</div>
|
|
438
|
-
|
|
439
|
-
<div class="card-list">
|
|
440
|
-
<div v-for="(config, name) in claudeConfigs" :key="name"
|
|
441
|
-
:class="['card', { active: currentClaudeConfig === name }]"
|
|
442
|
-
@click="applyClaudeConfig(name)">
|
|
443
|
-
<div class="card-leading">
|
|
444
|
-
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
|
|
445
|
-
<div class="card-content">
|
|
446
|
-
<div class="card-title">{{ name }}</div>
|
|
447
|
-
<div class="card-subtitle">{{ config.model || '未设置模型' }}</div>
|
|
448
|
-
</div>
|
|
449
|
-
</div>
|
|
450
|
-
<div class="card-trailing">
|
|
451
|
-
<span :class="['pill', config.hasKey ? 'configured' : 'empty']">
|
|
452
|
-
{{ config.hasKey ? '已配置' : '未配置' }}
|
|
453
|
-
</span>
|
|
454
|
-
<span v-if="claudeSpeedResults[name]" :class="['latency', claudeSpeedResults[name].ok ? 'ok' : 'error']">
|
|
455
|
-
{{ formatLatency(claudeSpeedResults[name]) }}
|
|
456
|
-
</span>
|
|
457
|
-
<div class="card-actions" @click.stop>
|
|
458
|
-
<button class="card-action-btn" @click="openEditConfigModal(name)" title="编辑">
|
|
459
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
460
|
-
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
461
|
-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
462
|
-
</svg>
|
|
463
|
-
</button>
|
|
464
|
-
<button class="card-action-btn" :class="{ loading: claudeShareLoading[name] }" @click="copyClaudeShareCommand(name)" title="分享导入命令" aria-label="Share import command">
|
|
465
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
466
|
-
<path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
|
|
467
|
-
<path d="M16 6l-4-4-4 4"/>
|
|
468
|
-
<path d="M12 2v14"/>
|
|
469
|
-
</svg>
|
|
470
|
-
</button>
|
|
471
|
-
<button class="card-action-btn delete" @click="deleteClaudeConfig(name)" title="删除">
|
|
472
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
473
|
-
<path d="M3 6h18"/>
|
|
474
|
-
<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"/>
|
|
475
|
-
</svg>
|
|
476
|
-
</button>
|
|
477
|
-
</div>
|
|
478
|
-
</div>
|
|
479
|
-
</div>
|
|
480
|
-
</div>
|
|
481
|
-
</div>
|
|
482
|
-
|
|
483
|
-
<!-- OpenClaw 配置模式 -->
|
|
484
|
-
<div
|
|
485
|
-
v-show="mainTab === 'config' && configMode === 'openclaw'"
|
|
486
|
-
class="mode-content mode-cards"
|
|
487
|
-
id="panel-config-openclaw"
|
|
488
|
-
role="tabpanel"
|
|
489
|
-
:aria-labelledby="'tab-config-openclaw'">
|
|
490
|
-
<button class="btn-add" @click="openOpenclawAddModal" v-if="!loading && !initError">
|
|
491
|
-
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
492
|
-
<path d="M10 4v12M4 10h12"/>
|
|
493
|
-
</svg>
|
|
494
|
-
添加 OpenClaw 配置
|
|
495
|
-
</button>
|
|
496
|
-
<div class="config-template-hint">
|
|
497
|
-
默认应用到 <code>~/.openclaw/openclaw.json</code>。支持 JSON5(注释/尾逗号)。
|
|
498
|
-
</div>
|
|
499
|
-
|
|
500
|
-
<div class="selector-section">
|
|
501
|
-
<div class="selector-header">
|
|
502
|
-
<span class="selector-title">AGENTS.md</span>
|
|
503
|
-
</div>
|
|
504
|
-
<div class="config-template-hint">
|
|
505
|
-
管理 OpenClaw Workspace 指令文件,默认读写 <code>~/.openclaw/workspace/AGENTS.md</code>。
|
|
506
|
-
</div>
|
|
507
|
-
<button class="btn-tool" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
508
|
-
{{ agentsLoading ? '加载中...' : '打开 AGENTS.md 编辑器' }}
|
|
509
|
-
</button>
|
|
510
|
-
</div>
|
|
511
|
-
|
|
512
|
-
<div class="selector-section">
|
|
513
|
-
<div class="selector-header">
|
|
514
|
-
<span class="selector-title">工作区文件</span>
|
|
515
|
-
</div>
|
|
516
|
-
<input
|
|
517
|
-
class="form-input"
|
|
518
|
-
v-model="openclawWorkspaceFileName"
|
|
519
|
-
placeholder="例如: SOUL.md">
|
|
520
|
-
<div class="config-template-hint">
|
|
521
|
-
仅支持 OpenClaw Workspace 内的 <code>.md</code> 文件。
|
|
522
|
-
</div>
|
|
523
|
-
<button class="btn-tool" @click="openOpenclawWorkspaceEditor" :disabled="loading || !!initError || agentsLoading">
|
|
524
|
-
{{ agentsLoading ? '加载中...' : '打开工作区文件' }}
|
|
525
|
-
</button>
|
|
526
|
-
</div>
|
|
527
|
-
|
|
528
|
-
<div class="card-list">
|
|
529
|
-
<div v-for="(config, name) in openclawConfigs" :key="name"
|
|
530
|
-
:class="['card', { active: currentOpenclawConfig === name }]"
|
|
531
|
-
@click="applyOpenclawConfig(name)">
|
|
532
|
-
<div class="card-leading">
|
|
533
|
-
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
|
|
534
|
-
<div class="card-content">
|
|
535
|
-
<div class="card-title">{{ name }}</div>
|
|
536
|
-
<div class="card-subtitle">{{ openclawSubtitle(config) }}</div>
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
<div class="card-trailing">
|
|
540
|
-
<span :class="['pill', openclawHasContent(config) ? 'configured' : 'empty']">
|
|
541
|
-
{{ openclawHasContent(config) ? '已配置' : '未配置' }}
|
|
542
|
-
</span>
|
|
543
|
-
<div class="card-actions" @click.stop>
|
|
544
|
-
<button class="card-action-btn" @click="openOpenclawEditModal(name)" title="编辑">
|
|
545
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
546
|
-
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
547
|
-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
548
|
-
</svg>
|
|
549
|
-
</button>
|
|
550
|
-
<button class="card-action-btn delete" @click="deleteOpenclawConfig(name)" title="删除">
|
|
551
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
552
|
-
<path d="M3 6h18"/>
|
|
553
|
-
<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"/>
|
|
554
|
-
</svg>
|
|
555
|
-
</button>
|
|
556
|
-
</div>
|
|
557
|
-
</div>
|
|
558
|
-
</div>
|
|
559
|
-
</div>
|
|
560
|
-
</div>
|
|
561
|
-
|
|
562
|
-
<!-- 会话浏览模式 -->
|
|
563
|
-
<div
|
|
564
|
-
v-show="mainTab === 'sessions'"
|
|
565
|
-
class="mode-content"
|
|
566
|
-
id="panel-sessions"
|
|
567
|
-
role="tabpanel"
|
|
568
|
-
:aria-labelledby="'tab-sessions'">
|
|
569
|
-
<div v-if="sessionStandalone" class="session-standalone-page">
|
|
570
|
-
<div v-if="sessionStandaloneLoading" class="state-message">
|
|
571
|
-
加载中...
|
|
572
|
-
</div>
|
|
573
|
-
<div v-else-if="sessionStandaloneError" class="state-message error">
|
|
574
|
-
{{ sessionStandaloneError }}
|
|
575
|
-
</div>
|
|
576
|
-
<div v-else>
|
|
577
|
-
<div class="session-standalone-title">
|
|
578
|
-
{{ sessionStandaloneTitle }}
|
|
579
|
-
<span v-if="sessionStandaloneSourceLabel"> · {{ sessionStandaloneSourceLabel }}</span>
|
|
580
|
-
</div>
|
|
581
|
-
<pre class="session-standalone-text">{{ sessionStandaloneText }}</pre>
|
|
582
|
-
</div>
|
|
583
|
-
</div>
|
|
584
|
-
|
|
585
|
-
<div v-else>
|
|
586
|
-
<div v-if="!sessionStandalone" class="selector-section">
|
|
587
|
-
<div class="selector-header">
|
|
588
|
-
<span class="selector-title">会话来源</span>
|
|
589
|
-
<div class="selector-actions">
|
|
590
|
-
<button class="btn-tool btn-tool-compact" @click="loadSessions" :disabled="sessionsLoading">
|
|
591
|
-
{{ sessionsLoading ? '刷新中...' : '刷新会话' }}
|
|
592
|
-
</button>
|
|
593
|
-
</div>
|
|
594
|
-
</div>
|
|
595
|
-
<div class="session-toolbar">
|
|
596
|
-
<div class="session-toolbar-group">
|
|
597
|
-
<select class="session-source-select" v-model="sessionFilterSource" @change="onSessionSourceChange" :disabled="sessionsLoading">
|
|
598
|
-
<option value="all">全部</option>
|
|
599
|
-
<option value="codex">Codex</option>
|
|
600
|
-
<option value="claude">Claude Code</option>
|
|
601
|
-
</select>
|
|
602
|
-
<select
|
|
603
|
-
class="session-path-select"
|
|
604
|
-
v-model="sessionPathFilter"
|
|
605
|
-
@change="onSessionPathFilterChange"
|
|
606
|
-
:disabled="sessionsLoading">
|
|
607
|
-
<option value="">全部路径</option>
|
|
608
|
-
<option v-for="cwd in sessionPathOptions" :key="cwd" :value="cwd">{{ cwd }}</option>
|
|
609
|
-
</select>
|
|
610
|
-
</div>
|
|
611
|
-
<div class="session-toolbar-group session-toolbar-grow">
|
|
612
|
-
<input
|
|
613
|
-
class="session-query-input"
|
|
614
|
-
v-model="sessionQuery"
|
|
615
|
-
@keyup.enter="loadSessions"
|
|
616
|
-
:disabled="sessionsLoading || !isSessionQueryEnabled"
|
|
617
|
-
:placeholder="sessionQueryPlaceholder">
|
|
618
|
-
</div>
|
|
619
|
-
<div class="session-toolbar-group">
|
|
620
|
-
<select
|
|
621
|
-
class="session-role-select"
|
|
622
|
-
v-model="sessionRoleFilter"
|
|
623
|
-
@change="onSessionFilterChange"
|
|
624
|
-
disabled>
|
|
625
|
-
<option value="all">全部角色</option>
|
|
626
|
-
<option value="user">仅 User</option>
|
|
627
|
-
<option value="assistant">仅 Assistant</option>
|
|
628
|
-
<option value="system">仅 System</option>
|
|
629
|
-
</select>
|
|
630
|
-
<select
|
|
631
|
-
class="session-time-select"
|
|
632
|
-
v-model="sessionTimePreset"
|
|
633
|
-
@change="onSessionFilterChange"
|
|
634
|
-
disabled>
|
|
635
|
-
<option value="all">全部时间</option>
|
|
636
|
-
<option value="7d">近 7 天</option>
|
|
637
|
-
<option value="30d">近 30 天</option>
|
|
638
|
-
<option value="90d">近 90 天</option>
|
|
639
|
-
</select>
|
|
640
|
-
</div>
|
|
641
|
-
</div>
|
|
642
|
-
<div class="session-toolbar-footer">
|
|
643
|
-
<label class="quick-option">
|
|
644
|
-
<input
|
|
645
|
-
type="checkbox"
|
|
646
|
-
v-model="sessionResumeWithYolo"
|
|
647
|
-
@change="onSessionResumeYoloChange"
|
|
648
|
-
>
|
|
649
|
-
复制恢复命令附带 --yolo
|
|
650
|
-
</label>
|
|
651
|
-
</div>
|
|
652
|
-
</div>
|
|
653
|
-
|
|
654
|
-
<div v-if="!sessionStandalone && sessionsLoading" class="state-message">
|
|
655
|
-
会话加载中...
|
|
656
|
-
</div>
|
|
657
|
-
|
|
658
|
-
<div v-else-if="!sessionStandalone && sessionsList.length === 0" class="session-empty">
|
|
659
|
-
暂无可用会话记录
|
|
660
|
-
</div>
|
|
661
|
-
|
|
662
|
-
<div v-else :class="['session-layout', { 'session-standalone': sessionStandalone }]">
|
|
663
|
-
<div v-if="!sessionStandalone" class="session-list">
|
|
664
|
-
<div
|
|
665
|
-
v-for="session in sessionsList"
|
|
666
|
-
:key="session.source + '-' + session.sessionId + '-' + session.filePath"
|
|
667
|
-
:class="[
|
|
668
|
-
'session-item',
|
|
669
|
-
{
|
|
670
|
-
active: activeSession && getSessionExportKey(activeSession) === getSessionExportKey(session)
|
|
671
|
-
}
|
|
672
|
-
]"
|
|
673
|
-
@click="selectSession(session)">
|
|
674
|
-
<div class="session-item-header">
|
|
675
|
-
<div class="session-item-main">
|
|
676
|
-
<div class="session-item-title">{{ session.title || session.sessionId }}</div>
|
|
677
|
-
<span class="session-count-badge">{{ session.messageCount ?? 0 }}</span>
|
|
678
|
-
</div>
|
|
679
|
-
<div class="session-item-actions">
|
|
680
|
-
<button
|
|
681
|
-
v-if="isResumeCommandAvailable(session)"
|
|
682
|
-
class="session-item-copy"
|
|
683
|
-
@click.stop="copyResumeCommand(session)"
|
|
684
|
-
:disabled="sessionsLoading"
|
|
685
|
-
aria-label="复制恢复命令"
|
|
686
|
-
title="复制恢复命令">
|
|
687
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
688
|
-
<rect x="8" y="8" width="12" height="12" rx="2"></rect>
|
|
689
|
-
<path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"></path>
|
|
690
|
-
</svg>
|
|
691
|
-
</button>
|
|
692
|
-
</div>
|
|
693
|
-
</div>
|
|
694
|
-
<div class="session-item-meta">
|
|
695
|
-
<span class="session-source">{{ session.sourceLabel }}</span>
|
|
696
|
-
<span class="session-item-time">{{ session.updatedAt || 'unknown time' }}</span>
|
|
697
|
-
</div>
|
|
698
|
-
</div>
|
|
699
|
-
</div>
|
|
700
|
-
|
|
701
|
-
<div :class="['session-preview', { active: !!activeSession }]">
|
|
702
|
-
<template v-if="activeSession">
|
|
703
|
-
<div class="session-preview-scroll">
|
|
704
|
-
<div class="session-preview-header">
|
|
705
|
-
<div>
|
|
706
|
-
<div class="session-preview-title">{{ activeSession.title || activeSession.sessionId }}</div>
|
|
707
|
-
<div class="session-preview-meta">
|
|
708
|
-
<span class="session-preview-meta-item">{{ activeSession.sourceLabel }}</span>
|
|
709
|
-
<span class="session-preview-meta-item">{{ activeSession.updatedAt || 'unknown time' }}</span>
|
|
710
|
-
</div>
|
|
711
|
-
<div class="session-preview-meta" v-if="activeSession.cwd">
|
|
712
|
-
<span class="session-preview-meta-item">{{ activeSession.cwd }}</span>
|
|
713
|
-
</div>
|
|
714
|
-
</div>
|
|
715
|
-
<div v-if="!sessionStandalone" class="session-actions">
|
|
716
|
-
<button class="btn-session-refresh" @click="loadActiveSessionDetail" :disabled="sessionDetailLoading || !activeSession">
|
|
717
|
-
{{ sessionDetailLoading ? '加载中...' : '刷新内容' }}
|
|
718
|
-
</button>
|
|
719
|
-
<button
|
|
720
|
-
v-if="isCloneAvailable(activeSession)"
|
|
721
|
-
class="btn-session-clone"
|
|
722
|
-
@click="cloneSession(activeSession)"
|
|
723
|
-
:disabled="!activeSession || sessionsLoading || sessionCloning[getSessionExportKey(activeSession)]">
|
|
724
|
-
{{ (activeSession && sessionCloning[getSessionExportKey(activeSession)]) ? '克隆中...' : '克隆会话' }}
|
|
725
|
-
</button>
|
|
726
|
-
<button
|
|
727
|
-
v-if="isDeleteAvailable(activeSession)"
|
|
728
|
-
class="btn-session-delete"
|
|
729
|
-
@click="deleteSession(activeSession)"
|
|
730
|
-
:disabled="!activeSession || sessionsLoading || sessionDeleting[getSessionExportKey(activeSession)]">
|
|
731
|
-
{{ (activeSession && sessionDeleting[getSessionExportKey(activeSession)]) ? '删除中...' : '删除会话' }}
|
|
732
|
-
</button>
|
|
733
|
-
<button
|
|
734
|
-
class="btn-session-export"
|
|
735
|
-
@click="exportSession(activeSession)"
|
|
736
|
-
:disabled="!activeSession || sessionExporting[getSessionExportKey(activeSession)]">
|
|
737
|
-
{{ (activeSession && sessionExporting[getSessionExportKey(activeSession)]) ? '导出中...' : '导出记录' }}
|
|
738
|
-
</button>
|
|
739
|
-
<button
|
|
740
|
-
v-if="!sessionStandalone"
|
|
741
|
-
class="btn-session-open"
|
|
742
|
-
@click="openSessionStandalone(activeSession)"
|
|
743
|
-
:disabled="!activeSession">
|
|
744
|
-
新页查看
|
|
745
|
-
</button>
|
|
746
|
-
</div>
|
|
747
|
-
</div>
|
|
748
|
-
|
|
749
|
-
<div v-if="sessionDetailLoading" class="session-preview-empty">
|
|
750
|
-
正在加载会话内容...
|
|
751
|
-
</div>
|
|
752
|
-
|
|
753
|
-
<div v-else-if="activeSessionDetailError" class="session-preview-empty">
|
|
754
|
-
{{ activeSessionDetailError }}
|
|
755
|
-
</div>
|
|
756
|
-
|
|
757
|
-
<div v-else-if="!activeSessionMessages.length" class="session-preview-empty">
|
|
758
|
-
当前会话暂无可展示消息
|
|
759
|
-
</div>
|
|
760
|
-
|
|
761
|
-
<div v-else class="session-preview-body">
|
|
762
|
-
<div v-if="activeSessionDetailClipped" class="session-item-sub session-item-wrap">
|
|
763
|
-
仅展示最近 {{ activeSessionMessages.length }} 条消息。
|
|
764
|
-
</div>
|
|
765
|
-
<div
|
|
766
|
-
v-for="(msg, idx) in activeSessionMessages"
|
|
767
|
-
:key="getRecordRenderKey(msg, idx)"
|
|
768
|
-
:class="['session-msg', msg.role === 'user' ? 'user' : (msg.role === 'system' ? 'system' : 'assistant')]">
|
|
769
|
-
<div class="session-msg-header">
|
|
770
|
-
<div class="session-msg-meta">
|
|
771
|
-
<span class="session-msg-role">{{ msg.role === 'user' ? 'User' : (msg.role === 'system' ? 'System' : 'Assistant') }}</span>
|
|
772
|
-
<span class="session-msg-time">{{ msg.timestamp || '' }}</span>
|
|
773
|
-
</div>
|
|
774
|
-
</div>
|
|
775
|
-
<div class="session-msg-content">{{ msg.text || '' }}</div>
|
|
776
|
-
</div>
|
|
777
|
-
</div>
|
|
778
|
-
</div>
|
|
779
|
-
</template>
|
|
780
|
-
|
|
781
|
-
<div v-else class="session-preview-empty">
|
|
782
|
-
<span v-if="sessionStandaloneError">{{ sessionStandaloneError }}</span>
|
|
783
|
-
<span v-else>请先在左侧选择一个会话</span>
|
|
784
|
-
</div>
|
|
785
|
-
</div>
|
|
786
|
-
</div>
|
|
787
|
-
</div>
|
|
788
|
-
</div>
|
|
789
|
-
|
|
790
|
-
<!-- 加载状态 -->
|
|
791
|
-
<div v-if="loading" class="state-message">
|
|
792
|
-
加载配置中...
|
|
793
|
-
</div>
|
|
794
|
-
|
|
795
|
-
<!-- 错误状态 -->
|
|
796
|
-
<div v-else-if="initError" class="state-message error">
|
|
797
|
-
{{ initError }}
|
|
798
|
-
</div>
|
|
799
|
-
</div>
|
|
800
|
-
|
|
801
|
-
</main>
|
|
802
|
-
</div>
|
|
803
|
-
|
|
804
|
-
<!-- 添加提供商模态框 -->
|
|
805
|
-
<div v-if="showAddModal" class="modal-overlay" @click.self="showAddModal = false">
|
|
806
|
-
<div class="modal">
|
|
807
|
-
<div class="modal-title">添加提供商</div>
|
|
808
|
-
|
|
809
|
-
<div class="form-group">
|
|
810
|
-
<label class="form-label">名称</label>
|
|
811
|
-
<input v-model="newProvider.name" class="form-input" placeholder="例如: myapi">
|
|
812
|
-
</div>
|
|
813
|
-
<div class="form-group">
|
|
814
|
-
<label class="form-label">API 端点</label>
|
|
815
|
-
<input v-model="newProvider.url" class="form-input" placeholder="https://api.example.com/v1">
|
|
816
|
-
</div>
|
|
817
|
-
<div class="form-group">
|
|
818
|
-
<label class="form-label">认证密钥</label>
|
|
819
|
-
<input v-model="newProvider.key" class="form-input" placeholder="sk-...">
|
|
820
|
-
</div>
|
|
821
|
-
|
|
822
|
-
<div class="btn-group">
|
|
823
|
-
<button class="btn btn-cancel" @click="closeAddModal">取消</button>
|
|
824
|
-
<button class="btn btn-confirm" @click="addProvider">添加</button>
|
|
825
|
-
</div>
|
|
826
|
-
</div>
|
|
827
|
-
</div>
|
|
828
|
-
|
|
829
|
-
<div v-if="showInstallModal" class="modal-overlay" @click.self="closeInstallModal">
|
|
830
|
-
<div class="modal">
|
|
831
|
-
<div class="modal-header">
|
|
832
|
-
<div class="modal-title">安装 CLI</div>
|
|
833
|
-
<button class="btn-mini" @click="closeInstallModal">关闭</button>
|
|
834
|
-
</div>
|
|
835
|
-
<div class="install-list">
|
|
836
|
-
<div
|
|
837
|
-
class="install-row"
|
|
838
|
-
v-for="cmd in installCommands"
|
|
839
|
-
:key="cmd">
|
|
840
|
-
<code class="install-command">{{ cmd }}</code>
|
|
841
|
-
<button class="btn-mini" @click="copyInstallCommand(cmd)">复制</button>
|
|
842
|
-
</div>
|
|
843
|
-
</div>
|
|
844
|
-
</div>
|
|
845
|
-
</div>
|
|
846
|
-
|
|
847
|
-
<!-- 编辑提供商模态框 -->
|
|
848
|
-
<div v-if="showEditModal" class="modal-overlay" @click.self="closeEditModal">
|
|
849
|
-
<div class="modal">
|
|
850
|
-
<div class="modal-title">编辑提供商</div>
|
|
851
|
-
|
|
852
|
-
<div class="form-group">
|
|
853
|
-
<label class="form-label">名称</label>
|
|
854
|
-
<input v-model="editingProvider.name" class="form-input" placeholder="提供商名称" readonly>
|
|
855
|
-
</div>
|
|
856
|
-
<div class="form-group">
|
|
857
|
-
<label class="form-label">API 端点</label>
|
|
858
|
-
<input v-model="editingProvider.url" class="form-input" placeholder="https://api.example.com/v1">
|
|
859
|
-
</div>
|
|
860
|
-
<div class="form-group">
|
|
861
|
-
<label class="form-label">认证密钥</label>
|
|
862
|
-
<input v-model="editingProvider.key" class="form-input" placeholder="留空则保持不变">
|
|
863
|
-
<div class="form-hint">留空表示不修改密钥</div>
|
|
864
|
-
</div>
|
|
865
|
-
|
|
866
|
-
<div class="btn-group">
|
|
867
|
-
<button class="btn btn-cancel" @click="closeEditModal">取消</button>
|
|
868
|
-
<button class="btn btn-confirm" @click="updateProvider">保存</button>
|
|
869
|
-
</div>
|
|
870
|
-
</div>
|
|
871
|
-
</div>
|
|
872
|
-
|
|
873
|
-
<!-- 添加模型模态框 -->
|
|
874
|
-
<div v-if="showModelModal" class="modal-overlay" @click.self="showModelModal = false">
|
|
875
|
-
<div class="modal">
|
|
876
|
-
<div class="modal-title">添加模型</div>
|
|
877
|
-
|
|
878
|
-
<div class="form-group">
|
|
879
|
-
<label class="form-label">模型名称</label>
|
|
880
|
-
<input v-model="newModelName" class="form-input" placeholder="例如: gpt-5">
|
|
881
|
-
</div>
|
|
882
|
-
|
|
883
|
-
<div class="btn-group">
|
|
884
|
-
<button class="btn btn-cancel" @click="closeModelModal">取消</button>
|
|
885
|
-
<button class="btn btn-confirm" @click="addModel">添加</button>
|
|
886
|
-
</div>
|
|
887
|
-
</div>
|
|
888
|
-
</div>
|
|
889
|
-
|
|
890
|
-
<!-- 模型列表模态框 -->
|
|
891
|
-
<div v-if="showModelListModal" class="modal-overlay" @click.self="showModelListModal = false">
|
|
892
|
-
<div class="modal">
|
|
893
|
-
<div class="modal-title">管理模型</div>
|
|
894
|
-
|
|
895
|
-
<div class="model-list">
|
|
896
|
-
<div v-for="model in models" :key="model" class="model-item">
|
|
897
|
-
<span>{{ model }}</span>
|
|
898
|
-
<span class="btn-remove-model" @click="removeModel(model)">删除</span>
|
|
899
|
-
</div>
|
|
900
|
-
</div>
|
|
901
|
-
|
|
902
|
-
<div class="btn-group">
|
|
903
|
-
<button class="btn btn-confirm" @click="showModelListModal = false">关闭</button>
|
|
904
|
-
</div>
|
|
905
|
-
</div>
|
|
906
|
-
</div>
|
|
907
|
-
|
|
908
|
-
<!-- 添加Claude配置模态框 -->
|
|
909
|
-
<div v-if="showClaudeConfigModal" class="modal-overlay" @click.self="showClaudeConfigModal = false">
|
|
910
|
-
<div class="modal">
|
|
911
|
-
<div class="modal-title">添加 Claude Code 配置</div>
|
|
912
|
-
|
|
913
|
-
<div class="form-group">
|
|
914
|
-
<label class="form-label">配置名称</label>
|
|
915
|
-
<input v-model="newClaudeConfig.name" class="form-input" placeholder="例如: 智谱GLM">
|
|
916
|
-
</div>
|
|
917
|
-
<div class="form-group">
|
|
918
|
-
<label class="form-label">API Key</label>
|
|
919
|
-
<input v-model="newClaudeConfig.apiKey" class="form-input" placeholder="sk-ant-...">
|
|
920
|
-
</div>
|
|
921
|
-
<div class="form-group">
|
|
922
|
-
<label class="form-label">Base URL</label>
|
|
923
|
-
<input v-model="newClaudeConfig.baseUrl" class="form-input" placeholder="https://open.bigmodel.cn/api/anthropic">
|
|
924
|
-
</div>
|
|
925
|
-
|
|
926
|
-
<div class="btn-group">
|
|
927
|
-
<button class="btn btn-cancel" @click="closeClaudeConfigModal">取消</button>
|
|
928
|
-
<button class="btn btn-confirm" @click="addClaudeConfig">添加</button>
|
|
929
|
-
</div>
|
|
930
|
-
</div>
|
|
931
|
-
</div>
|
|
932
|
-
|
|
933
|
-
<!-- 编辑Claude配置模态框 -->
|
|
934
|
-
<div v-if="showEditConfigModal" class="modal-overlay" @click.self="closeEditConfigModal">
|
|
935
|
-
<div class="modal">
|
|
936
|
-
<div class="modal-title">编辑 Claude Code 配置</div>
|
|
937
|
-
|
|
938
|
-
<div class="form-group">
|
|
939
|
-
<label class="form-label">配置名称</label>
|
|
940
|
-
<input v-model="editingConfig.name" class="form-input" placeholder="配置名称" readonly>
|
|
941
|
-
</div>
|
|
942
|
-
<div class="form-group">
|
|
943
|
-
<label class="form-label">API Key</label>
|
|
944
|
-
<input v-model="editingConfig.apiKey" class="form-input" placeholder="sk-ant-...">
|
|
945
|
-
</div>
|
|
946
|
-
<div class="form-group">
|
|
947
|
-
<label class="form-label">Base URL</label>
|
|
948
|
-
<input v-model="editingConfig.baseUrl" class="form-input" placeholder="https://open.bigmodel.cn/api/anthropic">
|
|
949
|
-
</div>
|
|
950
|
-
|
|
951
|
-
<div class="btn-group">
|
|
952
|
-
<button class="btn btn-cancel" @click="closeEditConfigModal">取消</button>
|
|
953
|
-
<button class="btn btn-confirm" @click="saveAndApplyConfig">保存并应用</button>
|
|
954
|
-
</div>
|
|
955
|
-
</div>
|
|
956
|
-
</div>
|
|
957
|
-
|
|
958
|
-
<div v-if="showOpenclawConfigModal" class="modal-overlay" @click.self="closeOpenclawConfigModal">
|
|
959
|
-
<div class="modal modal-wide">
|
|
960
|
-
<div class="modal-title">{{ openclawEditorTitle }}</div>
|
|
961
|
-
|
|
962
|
-
<div class="form-group">
|
|
963
|
-
<label class="form-label">配置名称</label>
|
|
964
|
-
<input v-model="openclawEditing.name" class="form-input" :readonly="openclawEditing.lockName" placeholder="例如: 默认配置">
|
|
965
|
-
</div>
|
|
966
|
-
|
|
967
|
-
<div class="form-group">
|
|
968
|
-
<label class="form-label">目标文件</label>
|
|
969
|
-
<div class="form-hint">
|
|
970
|
-
{{ openclawConfigPath || '未加载' }}
|
|
971
|
-
<span v-if="openclawConfigPath">
|
|
972
|
-
({{ openclawConfigExists ? '已存在' : '不存在,将在应用时创建' }})
|
|
973
|
-
</span>
|
|
974
|
-
</div>
|
|
975
|
-
<div class="btn-group" style="justify-content:flex-start;">
|
|
976
|
-
<button class="btn btn-confirm secondary" @click="loadOpenclawConfigFromFile" :disabled="openclawFileLoading">
|
|
977
|
-
{{ openclawFileLoading ? '加载中...' : '加载当前配置' }}
|
|
978
|
-
</button>
|
|
979
|
-
</div>
|
|
980
|
-
</div>
|
|
981
|
-
|
|
982
|
-
<div class="quick-section">
|
|
983
|
-
<div class="quick-header">
|
|
984
|
-
<div>
|
|
985
|
-
<div class="quick-title">新手快速配置</div>
|
|
986
|
-
<div class="form-hint">按 3 步完成:填 Provider 和模型,写入编辑器,保存并应用。</div>
|
|
987
|
-
</div>
|
|
988
|
-
<div class="quick-actions">
|
|
989
|
-
<button class="btn-mini" @click="syncOpenclawQuickFromText">从编辑器读取</button>
|
|
990
|
-
<button class="btn-mini" @click="resetOpenclawQuick">清空</button>
|
|
991
|
-
</div>
|
|
992
|
-
</div>
|
|
993
|
-
<div class="quick-steps">
|
|
994
|
-
<div class="quick-step"><span class="step-badge">1</span><span>填写 Provider 与模型</span></div>
|
|
995
|
-
<div class="quick-step"><span class="step-badge">2</span><span>点击写入编辑器</span></div>
|
|
996
|
-
<div class="quick-step"><span class="step-badge">3</span><span>保存并应用</span></div>
|
|
997
|
-
</div>
|
|
998
|
-
<div class="quick-grid">
|
|
999
|
-
<div class="quick-card">
|
|
1000
|
-
<div class="structured-card-title">Provider</div>
|
|
1001
|
-
<div class="form-group">
|
|
1002
|
-
<label class="form-label">Provider 名称</label>
|
|
1003
|
-
<input v-model="openclawQuick.providerName" class="form-input" placeholder="例如: custom-myapi">
|
|
1004
|
-
<div class="form-hint">会拼成 provider/model 作为主模型标识。</div>
|
|
1005
|
-
</div>
|
|
1006
|
-
<div class="form-group">
|
|
1007
|
-
<label class="form-label">Base URL</label>
|
|
1008
|
-
<input v-model="openclawQuick.baseUrl" class="form-input" placeholder="https://api.example.com/v1">
|
|
1009
|
-
</div>
|
|
1010
|
-
<div class="form-group">
|
|
1011
|
-
<label class="form-label">API Key</label>
|
|
1012
|
-
<div class="list-row">
|
|
1013
|
-
<input v-model="openclawQuick.apiKey" class="form-input" :type="openclawQuick.showKey ? 'text' : 'password'" placeholder="sk-...">
|
|
1014
|
-
<button class="btn-mini" @click="toggleOpenclawQuickKey">
|
|
1015
|
-
{{ openclawQuick.showKey ? '隐藏' : '显示' }}
|
|
1016
|
-
</button>
|
|
1017
|
-
</div>
|
|
1018
|
-
<div class="form-hint">留空表示不覆盖现有 key。</div>
|
|
1019
|
-
</div>
|
|
1020
|
-
<div class="form-group">
|
|
1021
|
-
<label class="form-label">API 类型</label>
|
|
1022
|
-
<input v-model="openclawQuick.apiType" class="form-input" list="openclawApiTypeList" placeholder="例如: openai-responses">
|
|
1023
|
-
<datalist id="openclawApiTypeList">
|
|
1024
|
-
<option value="openai-responses"></option>
|
|
1025
|
-
<option value="openai-chat"></option>
|
|
1026
|
-
<option value="anthropic"></option>
|
|
1027
|
-
<option value="custom"></option>
|
|
1028
|
-
</datalist>
|
|
1029
|
-
</div>
|
|
1030
|
-
</div>
|
|
1031
|
-
|
|
1032
|
-
<div class="quick-card">
|
|
1033
|
-
<div class="structured-card-title">模型</div>
|
|
1034
|
-
<div class="form-group">
|
|
1035
|
-
<label class="form-label">模型 ID</label>
|
|
1036
|
-
<input v-model="openclawQuick.modelId" class="form-input" placeholder="例如: gpt-4.1">
|
|
1037
|
-
</div>
|
|
1038
|
-
<div class="form-group">
|
|
1039
|
-
<label class="form-label">展示名称</label>
|
|
1040
|
-
<input v-model="openclawQuick.modelName" class="form-input" placeholder="留空则使用模型 ID">
|
|
1041
|
-
</div>
|
|
1042
|
-
<div class="form-group">
|
|
1043
|
-
<label class="form-label">上下文与最大输出</label>
|
|
1044
|
-
<div class="list-row">
|
|
1045
|
-
<input v-model="openclawQuick.contextWindow" class="form-input" placeholder="上下文长度">
|
|
1046
|
-
<input v-model="openclawQuick.maxTokens" class="form-input" placeholder="最大输出">
|
|
1047
|
-
</div>
|
|
1048
|
-
<div class="form-hint">留空表示不改动已有配置。</div>
|
|
1049
|
-
</div>
|
|
1050
|
-
</div>
|
|
1051
|
-
|
|
1052
|
-
<div class="quick-card">
|
|
1053
|
-
<div class="structured-card-title">选项</div>
|
|
1054
|
-
<label class="quick-option">
|
|
1055
|
-
<input type="checkbox" v-model="openclawQuick.setPrimary">
|
|
1056
|
-
设为主模型
|
|
1057
|
-
</label>
|
|
1058
|
-
<label class="quick-option">
|
|
1059
|
-
<input type="checkbox" v-model="openclawQuick.overrideProvider">
|
|
1060
|
-
覆盖同名 Provider 基础信息
|
|
1061
|
-
</label>
|
|
1062
|
-
<label class="quick-option">
|
|
1063
|
-
<input type="checkbox" v-model="openclawQuick.overrideModels">
|
|
1064
|
-
覆盖同名模型列表
|
|
1065
|
-
</label>
|
|
1066
|
-
<div class="form-hint">关闭覆盖会只补空缺字段。</div>
|
|
1067
|
-
</div>
|
|
1068
|
-
</div>
|
|
1069
|
-
<div class="btn-group">
|
|
1070
|
-
<button class="btn btn-confirm" @click="applyOpenclawQuickToText">写入编辑器</button>
|
|
1071
|
-
</div>
|
|
1072
|
-
</div>
|
|
1073
|
-
|
|
1074
|
-
<div class="structured-section">
|
|
1075
|
-
<div class="structured-header">
|
|
1076
|
-
<span class="structured-title">结构化配置(高级)</span>
|
|
1077
|
-
<span class="form-hint">写入编辑器会重排 JSON,注释可能丢失。</span>
|
|
1078
|
-
</div>
|
|
1079
|
-
<div class="structured-grid">
|
|
1080
|
-
<div class="structured-card">
|
|
1081
|
-
<div class="structured-card-title">Agents Defaults</div>
|
|
1082
|
-
<div class="form-group">
|
|
1083
|
-
<label class="form-label">主模型</label>
|
|
1084
|
-
<input v-model="openclawStructured.agentPrimary" class="form-input" placeholder="例如: provider/model">
|
|
1085
|
-
</div>
|
|
1086
|
-
<div class="form-group">
|
|
1087
|
-
<label class="form-label">备选模型</label>
|
|
1088
|
-
<div class="list-row" v-for="(item, index) in openclawStructured.agentFallbacks" :key="'fallback-' + index">
|
|
1089
|
-
<input v-model="openclawStructured.agentFallbacks[index]" class="form-input" placeholder="例如: provider/model">
|
|
1090
|
-
<button class="btn-mini delete" @click="removeOpenclawFallback(index)">删除</button>
|
|
1091
|
-
</div>
|
|
1092
|
-
<button class="btn-mini" @click="addOpenclawFallback">添加备选</button>
|
|
1093
|
-
</div>
|
|
1094
|
-
<div class="form-group">
|
|
1095
|
-
<label class="form-label">Workspace</label>
|
|
1096
|
-
<input v-model="openclawStructured.workspace" class="form-input" placeholder="例如: ~/.openclaw/workspace">
|
|
1097
|
-
</div>
|
|
1098
|
-
<div class="form-group">
|
|
1099
|
-
<label class="form-label">Timeout(s)</label>
|
|
1100
|
-
<input v-model="openclawStructured.timeout" class="form-input" placeholder="例如: 600">
|
|
1101
|
-
</div>
|
|
1102
|
-
<div class="form-group">
|
|
1103
|
-
<label class="form-label">Context Tokens</label>
|
|
1104
|
-
<input v-model="openclawStructured.contextTokens" class="form-input" placeholder="例如: 4096">
|
|
1105
|
-
</div>
|
|
1106
|
-
<div class="form-group">
|
|
1107
|
-
<label class="form-label">Max Concurrent</label>
|
|
1108
|
-
<input v-model="openclawStructured.maxConcurrent" class="form-input" placeholder="例如: 2">
|
|
1109
|
-
</div>
|
|
1110
|
-
</div>
|
|
1111
|
-
|
|
1112
|
-
<div class="structured-card">
|
|
1113
|
-
<div class="structured-card-title">Env</div>
|
|
1114
|
-
<div class="form-group">
|
|
1115
|
-
<label class="form-label">环境变量</label>
|
|
1116
|
-
<div class="list-row" v-for="(item, index) in openclawStructured.envItems" :key="'env-' + index">
|
|
1117
|
-
<input v-model="item.key" class="form-input" placeholder="KEY">
|
|
1118
|
-
<input v-model="item.value" class="form-input" :type="item.show ? 'text' : 'password'" placeholder="VALUE">
|
|
1119
|
-
<button class="btn-mini" @click="toggleOpenclawEnvItem(index)">
|
|
1120
|
-
{{ item.show ? '隐藏' : '显示' }}
|
|
1121
|
-
</button>
|
|
1122
|
-
<button class="btn-mini delete" @click="removeOpenclawEnvItem(index)">删除</button>
|
|
1123
|
-
</div>
|
|
1124
|
-
<button class="btn-mini" @click="addOpenclawEnvItem">添加变量</button>
|
|
1125
|
-
</div>
|
|
1126
|
-
</div>
|
|
1127
|
-
|
|
1128
|
-
<div class="structured-card">
|
|
1129
|
-
<div class="structured-card-title">Tools</div>
|
|
1130
|
-
<div class="form-group">
|
|
1131
|
-
<label class="form-label">Profile</label>
|
|
1132
|
-
<select v-model="openclawStructured.toolsProfile" class="form-input">
|
|
1133
|
-
<option value="default">default</option>
|
|
1134
|
-
<option value="strict">strict</option>
|
|
1135
|
-
<option value="permissive">permissive</option>
|
|
1136
|
-
<option value="custom">custom</option>
|
|
1137
|
-
</select>
|
|
1138
|
-
</div>
|
|
1139
|
-
<div class="form-group">
|
|
1140
|
-
<label class="form-label">Allow</label>
|
|
1141
|
-
<div class="list-row" v-for="(item, index) in openclawStructured.toolsAllow" :key="'allow-' + index">
|
|
1142
|
-
<input v-model="openclawStructured.toolsAllow[index]" class="form-input" placeholder="例如: fs.read*">
|
|
1143
|
-
<button class="btn-mini delete" @click="removeOpenclawToolsAllow(index)">删除</button>
|
|
1144
|
-
</div>
|
|
1145
|
-
<button class="btn-mini" @click="addOpenclawToolsAllow">添加 allow</button>
|
|
1146
|
-
</div>
|
|
1147
|
-
<div class="form-group">
|
|
1148
|
-
<label class="form-label">Deny</label>
|
|
1149
|
-
<div class="list-row" v-for="(item, index) in openclawStructured.toolsDeny" :key="'deny-' + index">
|
|
1150
|
-
<input v-model="openclawStructured.toolsDeny[index]" class="form-input" placeholder="例如: net.*">
|
|
1151
|
-
<button class="btn-mini delete" @click="removeOpenclawToolsDeny(index)">删除</button>
|
|
1152
|
-
</div>
|
|
1153
|
-
<button class="btn-mini" @click="addOpenclawToolsDeny">添加 deny</button>
|
|
1154
|
-
</div>
|
|
1155
|
-
</div>
|
|
1156
|
-
|
|
1157
|
-
<div class="structured-card">
|
|
1158
|
-
<div class="structured-card-title">Providers(只读)</div>
|
|
1159
|
-
<div v-if="openclawProviders.length === 0" class="form-hint">
|
|
1160
|
-
未发现 providers 配置(可能使用内置 provider 或 auth profiles)。
|
|
1161
|
-
</div>
|
|
1162
|
-
<div v-else class="provider-list">
|
|
1163
|
-
<div class="provider-item" v-for="(provider, index) in openclawProviders" :key="provider.key + '-' + provider.source + '-' + index">
|
|
1164
|
-
<div class="provider-header">
|
|
1165
|
-
<span class="provider-name">{{ provider.key }}</span>
|
|
1166
|
-
<span class="provider-source">来源: {{ provider.source }}</span>
|
|
1167
|
-
<span v-if="provider.isActive" class="pill configured">使用中</span>
|
|
1168
|
-
</div>
|
|
1169
|
-
<div v-if="provider.fields.length === 0" class="form-hint">未配置字段</div>
|
|
1170
|
-
<div v-else class="provider-fields">
|
|
1171
|
-
<div class="provider-field" v-for="field in provider.fields" :key="provider.key + '-' + field.key">
|
|
1172
|
-
<span class="provider-field-key">{{ field.key }}</span>
|
|
1173
|
-
<span class="provider-field-value">{{ field.value }}</span>
|
|
1174
|
-
</div>
|
|
1175
|
-
</div>
|
|
1176
|
-
</div>
|
|
1177
|
-
</div>
|
|
1178
|
-
<div v-if="openclawMissingProviders.length" class="form-hint">
|
|
1179
|
-
使用中的 provider 未在配置中显示:{{ openclawMissingProviders.join(', ') }}。
|
|
1180
|
-
</div>
|
|
1181
|
-
</div>
|
|
1182
|
-
|
|
1183
|
-
<div class="structured-card">
|
|
1184
|
-
<div class="structured-card-title">Agents(只读)</div>
|
|
1185
|
-
<div v-if="openclawAgentsList.length === 0" class="form-hint">
|
|
1186
|
-
未发现 agents.list 配置。
|
|
1187
|
-
</div>
|
|
1188
|
-
<div v-else class="agent-list">
|
|
1189
|
-
<div class="agent-item" v-for="(agent, index) in openclawAgentsList" :key="agent.key + '-' + index">
|
|
1190
|
-
<div class="agent-header">
|
|
1191
|
-
<span class="agent-name">{{ agent.name }}</span>
|
|
1192
|
-
<span class="agent-id">ID: {{ agent.id }}</span>
|
|
1193
|
-
</div>
|
|
1194
|
-
<div class="agent-meta" v-if="agent.theme || agent.emoji || agent.avatar">
|
|
1195
|
-
<span v-if="agent.theme">主题: {{ agent.theme }}</span>
|
|
1196
|
-
<span v-if="agent.emoji">表情: {{ agent.emoji }}</span>
|
|
1197
|
-
<span v-if="agent.avatar">头像: {{ agent.avatar }}</span>
|
|
1198
|
-
</div>
|
|
1199
|
-
</div>
|
|
1200
|
-
</div>
|
|
1201
|
-
</div>
|
|
1202
|
-
</div>
|
|
1203
|
-
<div class="btn-group">
|
|
1204
|
-
<button class="btn btn-confirm secondary" @click="syncOpenclawStructuredFromText">从文本刷新</button>
|
|
1205
|
-
<button class="btn btn-confirm" @click="applyOpenclawStructuredToText">写入编辑器</button>
|
|
1206
|
-
</div>
|
|
1207
|
-
</div>
|
|
1208
|
-
|
|
1209
|
-
<div class="form-group">
|
|
1210
|
-
<label class="form-label">OpenClaw 配置(JSON5)</label>
|
|
1211
|
-
<textarea
|
|
1212
|
-
v-model="openclawEditing.content"
|
|
1213
|
-
class="form-input template-editor"
|
|
1214
|
-
spellcheck="false"
|
|
1215
|
-
placeholder="在这里编辑 OpenClaw 配置(JSON5)"></textarea>
|
|
1216
|
-
<div class="template-editor-warning">
|
|
1217
|
-
保存仅写入本地配置库。点击“保存并应用”后会写入 openclaw.json。
|
|
1218
|
-
</div>
|
|
1219
|
-
</div>
|
|
1220
|
-
|
|
1221
|
-
<div class="btn-group">
|
|
1222
|
-
<button class="btn btn-cancel" @click="closeOpenclawConfigModal">取消</button>
|
|
1223
|
-
<button class="btn btn-confirm" @click="saveOpenclawConfig" :disabled="openclawSaving">
|
|
1224
|
-
{{ openclawSaving ? '保存中...' : '保存' }}
|
|
1225
|
-
</button>
|
|
1226
|
-
<button class="btn btn-confirm secondary" @click="saveAndApplyOpenclawConfig" :disabled="openclawApplying">
|
|
1227
|
-
{{ openclawApplying ? '应用中...' : '保存并应用' }}
|
|
1228
|
-
</button>
|
|
1229
|
-
</div>
|
|
1230
|
-
</div>
|
|
1231
|
-
</div>
|
|
1232
|
-
|
|
1233
|
-
<div v-if="showConfigTemplateModal" class="modal-overlay" @click.self="closeConfigTemplateModal">
|
|
1234
|
-
<div class="modal modal-wide">
|
|
1235
|
-
<div class="modal-title">Config 模板编辑器(手动确认应用)</div>
|
|
1236
|
-
|
|
1237
|
-
<div class="form-group">
|
|
1238
|
-
<label class="form-label">config.toml 模板</label>
|
|
1239
|
-
<textarea
|
|
1240
|
-
v-model="configTemplateContent"
|
|
1241
|
-
class="form-input template-editor"
|
|
1242
|
-
spellcheck="false"
|
|
1243
|
-
placeholder="在这里编辑 config.toml 模板内容"></textarea>
|
|
1244
|
-
<div class="template-editor-warning">
|
|
1245
|
-
工具不会自动改动 `config.toml`。只有点击“确认应用模板”后才写入。
|
|
1246
|
-
</div>
|
|
1247
|
-
</div>
|
|
1248
|
-
|
|
1249
|
-
<div class="btn-group">
|
|
1250
|
-
<button class="btn btn-cancel" @click="closeConfigTemplateModal">取消</button>
|
|
1251
|
-
<button class="btn btn-confirm" @click="applyConfigTemplate" :disabled="configTemplateApplying">
|
|
1252
|
-
{{ configTemplateApplying ? '应用中...' : '确认应用模板' }}
|
|
1253
|
-
</button>
|
|
1254
|
-
</div>
|
|
1255
|
-
</div>
|
|
1256
|
-
</div>
|
|
1257
|
-
|
|
1258
|
-
<div v-if="showAgentsModal" class="modal-overlay" @click.self="closeAgentsModal">
|
|
1259
|
-
<div class="modal modal-wide">
|
|
1260
|
-
<div class="modal-header">
|
|
1261
|
-
<div class="modal-title">{{ agentsModalTitle }}</div>
|
|
1262
|
-
<button
|
|
1263
|
-
class="btn-mini btn-modal-copy"
|
|
1264
|
-
@click="copyAgentsContent"
|
|
1265
|
-
:disabled="agentsLoading">
|
|
1266
|
-
复制
|
|
1267
|
-
</button>
|
|
1268
|
-
</div>
|
|
1269
|
-
|
|
1270
|
-
<div class="form-group">
|
|
1271
|
-
<label class="form-label">目标文件</label>
|
|
1272
|
-
<div class="form-hint">
|
|
1273
|
-
{{ agentsPath || '未加载' }}
|
|
1274
|
-
<span v-if="agentsPath">
|
|
1275
|
-
({{ agentsExists ? '已存在' : '不存在,将在保存时创建' }})
|
|
1276
|
-
</span>
|
|
1277
|
-
</div>
|
|
1278
|
-
</div>
|
|
1279
|
-
|
|
1280
|
-
<div class="form-group">
|
|
1281
|
-
<label class="form-label">AGENTS.md 内容</label>
|
|
1282
|
-
<textarea
|
|
1283
|
-
v-model="agentsContent"
|
|
1284
|
-
class="form-input template-editor"
|
|
1285
|
-
spellcheck="false"
|
|
1286
|
-
:readonly="agentsLoading"
|
|
1287
|
-
placeholder="在这里编辑 AGENTS.md 内容"></textarea>
|
|
1288
|
-
<div class="template-editor-warning">
|
|
1289
|
-
{{ agentsModalHint }}
|
|
1290
|
-
</div>
|
|
1291
|
-
</div>
|
|
1292
|
-
|
|
1293
|
-
<div class="btn-group">
|
|
1294
|
-
<button class="btn btn-cancel" @click="closeAgentsModal">取消</button>
|
|
1295
|
-
<button class="btn btn-confirm" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading">
|
|
1296
|
-
{{ agentsSaving ? '保存中...' : '保存' }}
|
|
1297
|
-
</button>
|
|
1298
|
-
</div>
|
|
1299
|
-
</div>
|
|
1300
|
-
</div>
|
|
1301
|
-
|
|
1302
|
-
<!-- Toast通知 -->
|
|
1303
|
-
|
|
1304
|
-
<!-- Toast -->
|
|
1305
|
-
<div v-if="message" :class="['toast', messageType]">{{ message }}</div>
|
|
1306
|
-
</div>
|
|
1307
|
-
|
|
1308
|
-
<script type="module" src="web-ui/app.js"></script>
|
|
1309
|
-
</body>
|
|
1310
|
-
</html>
|