openclaw-config-web 1.0.0

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.
@@ -0,0 +1,157 @@
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>OpenClaw 模型配置</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <header>
12
+ <h1>🦀 OpenClaw 模型配置</h1>
13
+ <div class="status" id="status"></div>
14
+ </header>
15
+
16
+ <section class="current-model">
17
+ <h2>当前主模型</h2>
18
+ <div class="model-display" id="currentModel">加载中...</div>
19
+ </section>
20
+
21
+ <section class="model-list">
22
+ <h2>可用模型 <button class="btn-refresh" onclick="loadConfig()">刷新</button></h2>
23
+ <div id="modelList">加载中...</div>
24
+ </section>
25
+
26
+ <div id="editModal" class="modal">
27
+ <div class="modal-content">
28
+ <div class="modal-header">
29
+ <h3 id="modalTitle">编辑模型</h3>
30
+ <span class="modal-close" onclick="closeModal()">&times;</span>
31
+ </div>
32
+ <div class="modal-body" id="modalBody"></div>
33
+ </div>
34
+ </div>
35
+
36
+ <section class="add-provider">
37
+ <h2>添加模型提供商</h2>
38
+ <form id="providerForm">
39
+ <div class="form-row">
40
+ <label>提供商名称</label>
41
+ <input type="text" id="providerName" placeholder="如: openai-custom">
42
+ </div>
43
+ <div class="form-row">
44
+ <label>API 类型</label>
45
+ <select id="apiType">
46
+ <option value="openai-completions">OpenAI Completions</option>
47
+ <option value="openai-responses">OpenAI Responses</option>
48
+ <option value="openai-codex-responses">OpenAI Codex Responses</option>
49
+ <option value="anthropic-messages">Anthropic Messages (Claude)</option>
50
+ <option value="azure-openai-responses">Azure OpenAI Responses</option>
51
+ <option value="google-generative-ai">Google Generative AI</option>
52
+ <option value="google-gemini-cli">Google Gemini CLI</option>
53
+ <option value="google-vertex">Google Vertex AI</option>
54
+ <option value="bedrock-converse-stream">AWS Bedrock Converse</option>
55
+ </select>
56
+ </div>
57
+ <div class="form-row">
58
+ <label>Base URL</label>
59
+ <input type="text" id="providerUrl" placeholder="https://api.openai.com/v1">
60
+ </div>
61
+ <div class="form-row">
62
+ <label>API Key</label>
63
+ <div class="input-with-btn">
64
+ <input type="password" id="providerKey" placeholder="sk-...">
65
+ <button type="button" class="btn btn-secondary btn-small" onclick="toggleApiKeyVisibility('providerKey', this)">显示</button>
66
+ </div>
67
+ </div>
68
+ <div class="form-row">
69
+ <label>模型 ID</label>
70
+ <input type="text" id="modelId" placeholder="gpt-4">
71
+ </div>
72
+ <div class="form-row">
73
+ <label>模型名称</label>
74
+ <input type="text" id="modelName" placeholder="GPT-4">
75
+ </div>
76
+ <div class="form-row">
77
+ <label>上下文窗口</label>
78
+ <input type="number" id="contextWindow" value="128000">
79
+ </div>
80
+ <button type="submit" class="btn-primary">添加提供商</button>
81
+ </form>
82
+ </section>
83
+
84
+ <section class="channel-config">
85
+ <h2>渠道配置</h2>
86
+ <div class="channel-tabs">
87
+ <button class="tab-btn active" onclick="switchChannelTab('feishu')">飞书</button>
88
+ <button class="tab-btn" onclick="switchChannelTab('dingtalk')">钉钉</button>
89
+ </div>
90
+ <div id="feishuConfig" class="channel-content">
91
+ <form id="feishuForm">
92
+ <div class="form-row">
93
+ <label>App ID</label>
94
+ <input type="text" id="feishuAppId" placeholder="cli_xxxxxxxxx">
95
+ </div>
96
+ <div class="form-row">
97
+ <label>App Secret</label>
98
+ <div class="input-with-btn">
99
+ <input type="password" id="feishuAppSecret" placeholder="xxxxxxxxxxxxxx">
100
+ <button type="button" class="btn btn-secondary btn-small" onclick="toggleApiKeyVisibility('feishuAppSecret', this)">显示</button>
101
+ </div>
102
+ </div>
103
+ <button type="submit" class="btn-primary">保存飞书配置</button>
104
+ </form>
105
+ </div>
106
+ <div id="dingtalkConfig" class="channel-content" style="display: none;">
107
+ <form id="dingtalkForm">
108
+ <div class="form-row">
109
+ <label>App Key</label>
110
+ <input type="text" id="dingtalkAppKey" placeholder="dingxxxxxxxxxxxx">
111
+ </div>
112
+ <div class="form-row">
113
+ <label>App Secret</label>
114
+ <div class="input-with-btn">
115
+ <input type="password" id="dingtalkAppSecret" placeholder="xxxxxxxxxxxxxx">
116
+ <button type="button" class="btn btn-secondary btn-small" onclick="toggleApiKeyVisibility('dingtalkAppSecret', this)">显示</button>
117
+ </div>
118
+ </div>
119
+ <button type="submit" class="btn-primary">保存钉钉配置</button>
120
+ </form>
121
+ </div>
122
+ </section>
123
+
124
+ <section class="service-control">
125
+ <h2>网关服务 <span id="serviceStatus" class="service-status">检查中...</span></h2>
126
+ <div class="btn-group">
127
+ <button class="btn btn-success" id="btnStart" onclick="serviceAction('start')">启动</button>
128
+ <button class="btn btn-danger" id="btnStop" onclick="serviceAction('stop')">停止</button>
129
+ <button class="btn btn-primary" id="btnRestart" onclick="serviceAction('restart')">重启</button>
130
+ <button class="btn btn-secondary" onclick="updateServiceStatus()">刷新状态</button>
131
+ </div>
132
+ </section>
133
+
134
+ <section class="openclaw-functions">
135
+ <h2>OpenClaw 功能</h2>
136
+ <div class="btn-group">
137
+ <button class="btn btn-primary" onclick="runOpenClawCommand('doctor')">健康检查</button>
138
+ <button class="btn btn-primary" onclick="runOpenClawCommand('status')">状态查询</button>
139
+ <button class="btn btn-primary" onclick="runOpenClawCommand('configure')">配置向导</button>
140
+ <button class="btn btn-primary" onclick="runOpenClawCommand('dashboard')">控制面板</button>
141
+ <button class="btn btn-primary" onclick="runOpenClawCommand('tui')">终端UI</button>
142
+ <button class="btn btn-primary" onclick="runOpenClawCommand('models list')">模型列表</button>
143
+ <button class="btn btn-primary" onclick="runOpenClawCommand('channels')">渠道管理</button>
144
+ <button class="btn btn-primary" onclick="runOpenClawCommand('logs')">查看日志</button>
145
+ <button class="btn btn-primary" onclick="runOpenClawCommand('health')">健康状态</button>
146
+ </div>
147
+ <div id="functionOutput" class="function-output" style="display: none;">
148
+ <pre id="outputContent"></pre>
149
+ <button class="btn btn-secondary btn-small" onclick="closeFunctionOutput()">关闭</button>
150
+ </div>
151
+ </section>
152
+ </div>
153
+
154
+ <div class="toast" id="toast"></div>
155
+ <script src="app.js"></script>
156
+ </body>
157
+ </html>
@@ -0,0 +1,508 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
10
+ color: #e4e4e4;
11
+ min-height: 100vh;
12
+ padding: 20px;
13
+ }
14
+
15
+ .container {
16
+ max-width: 900px;
17
+ margin: 0 auto;
18
+ }
19
+
20
+ header {
21
+ display: flex;
22
+ justify-content: space-between;
23
+ align-items: center;
24
+ margin-bottom: 30px;
25
+ padding-bottom: 20px;
26
+ border-bottom: 1px solid #333;
27
+ }
28
+
29
+ h1 {
30
+ font-size: 1.8rem;
31
+ color: #00d4ff;
32
+ }
33
+
34
+ h2 {
35
+ font-size: 1.2rem;
36
+ margin-bottom: 15px;
37
+ color: #00d4ff;
38
+ }
39
+
40
+ section {
41
+ background: rgba(255, 255, 255, 0.05);
42
+ border-radius: 12px;
43
+ padding: 20px;
44
+ margin-bottom: 20px;
45
+ border: 1px solid rgba(255, 255, 255, 0.1);
46
+ }
47
+
48
+ .status {
49
+ padding: 6px 12px;
50
+ border-radius: 20px;
51
+ font-size: 0.85rem;
52
+ background: #2a2a4a;
53
+ }
54
+
55
+ .status.connected {
56
+ background: #0a5c36;
57
+ color: #4ade80;
58
+ }
59
+
60
+ .current-model .model-display {
61
+ font-size: 1.5rem;
62
+ font-weight: bold;
63
+ color: #4ade80;
64
+ padding: 15px;
65
+ background: rgba(74, 222, 128, 0.1);
66
+ border-radius: 8px;
67
+ border-left: 4px solid #4ade80;
68
+ }
69
+
70
+ .primary-model {
71
+ font-size: 1.3rem;
72
+ font-weight: bold;
73
+ color: #4ade80;
74
+ }
75
+
76
+ .fallback-label {
77
+ font-size: 0.85rem;
78
+ color: #888;
79
+ margin-top: 10px;
80
+ margin-bottom: 5px;
81
+ }
82
+
83
+ .fallback-list {
84
+ display: flex;
85
+ flex-wrap: wrap;
86
+ gap: 6px;
87
+ }
88
+
89
+ .fallback-tag {
90
+ background: rgba(251, 191, 36, 0.2);
91
+ color: #fbbf24;
92
+ padding: 4px 10px;
93
+ border-radius: 12px;
94
+ font-size: 0.85rem;
95
+ }
96
+
97
+ .no-fallback {
98
+ font-size: 0.85rem;
99
+ color: #666;
100
+ margin-top: 8px;
101
+ }
102
+
103
+ .model-card.fallback {
104
+ border-color: #fbbf24;
105
+ background: rgba(251, 191, 36, 0.05);
106
+ }
107
+
108
+ .btn-warning {
109
+ background: #f59e0b;
110
+ color: #000;
111
+ }
112
+
113
+ .btn-warning:hover {
114
+ background: #d97706;
115
+ }
116
+
117
+ .btn-outline {
118
+ background: transparent;
119
+ border: 1px solid #fbbf24;
120
+ color: #fbbf24;
121
+ }
122
+
123
+ .btn-outline:hover {
124
+ background: rgba(251, 191, 36, 0.1);
125
+ }
126
+
127
+ .model-grid {
128
+ display: grid;
129
+ gap: 12px;
130
+ }
131
+
132
+ .model-card {
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ padding: 15px;
137
+ background: rgba(255, 255, 255, 0.05);
138
+ border-radius: 8px;
139
+ border: 1px solid rgba(255, 255, 255, 0.1);
140
+ transition: all 0.2s;
141
+ }
142
+
143
+ .model-card:hover {
144
+ background: rgba(255, 255, 255, 0.1);
145
+ border-color: #00d4ff;
146
+ }
147
+
148
+ .model-card.active {
149
+ border-color: #4ade80;
150
+ background: rgba(74, 222, 128, 0.1);
151
+ }
152
+
153
+ .model-info {
154
+ flex: 1;
155
+ }
156
+
157
+ .model-name {
158
+ font-weight: 600;
159
+ font-size: 1.1rem;
160
+ }
161
+
162
+ .model-provider {
163
+ font-size: 0.85rem;
164
+ color: #888;
165
+ margin-top: 4px;
166
+ }
167
+
168
+ .model-meta {
169
+ font-size: 0.8rem;
170
+ color: #666;
171
+ margin-top: 6px;
172
+ }
173
+
174
+ .btn {
175
+ padding: 8px 16px;
176
+ border: none;
177
+ border-radius: 6px;
178
+ cursor: pointer;
179
+ font-size: 0.9rem;
180
+ transition: all 0.2s;
181
+ }
182
+
183
+ .btn-primary {
184
+ background: #00d4ff;
185
+ color: #000;
186
+ }
187
+
188
+ .btn-primary:hover {
189
+ background: #00b8e6;
190
+ }
191
+
192
+ .btn-success {
193
+ background: #4ade80;
194
+ color: #000;
195
+ }
196
+
197
+ .btn-success:hover {
198
+ background: #22c55e;
199
+ }
200
+
201
+ .btn-danger {
202
+ background: #ef4444;
203
+ color: #fff;
204
+ }
205
+
206
+ .btn-danger:hover {
207
+ background: #dc2626;
208
+ }
209
+
210
+ .btn-refresh {
211
+ background: transparent;
212
+ border: 1px solid #00d4ff;
213
+ color: #00d4ff;
214
+ padding: 4px 12px;
215
+ font-size: 0.8rem;
216
+ margin-left: 10px;
217
+ }
218
+
219
+ .form-row {
220
+ margin-bottom: 15px;
221
+ }
222
+
223
+ .form-row label {
224
+ display: block;
225
+ margin-bottom: 5px;
226
+ font-size: 0.9rem;
227
+ color: #aaa;
228
+ }
229
+
230
+ .form-row input {
231
+ width: 100%;
232
+ padding: 10px 12px;
233
+ border: 1px solid #333;
234
+ border-radius: 6px;
235
+ background: rgba(255, 255, 255, 0.05);
236
+ color: #e4e4e4;
237
+ font-size: 0.95rem;
238
+ }
239
+
240
+ .form-row input:focus {
241
+ outline: none;
242
+ border-color: #00d4ff;
243
+ }
244
+
245
+ .toast {
246
+ position: fixed;
247
+ bottom: 30px;
248
+ right: 30px;
249
+ padding: 12px 24px;
250
+ border-radius: 8px;
251
+ background: #333;
252
+ color: #fff;
253
+ opacity: 0;
254
+ transform: translateY(20px);
255
+ transition: all 0.3s;
256
+ z-index: 1000;
257
+ }
258
+
259
+ .toast.show {
260
+ opacity: 1;
261
+ transform: translateY(0);
262
+ }
263
+
264
+ .toast.success {
265
+ background: #0a5c36;
266
+ }
267
+
268
+ .toast.error {
269
+ background: #7f1d1d;
270
+ }
271
+
272
+ .service-status {
273
+ font-size: 0.9rem;
274
+ padding: 4px 10px;
275
+ border-radius: 12px;
276
+ margin-left: 10px;
277
+ vertical-align: middle;
278
+ }
279
+
280
+ .service-status.running {
281
+ background: #0a5c36;
282
+ color: #4ade80;
283
+ }
284
+
285
+ .service-status.stopped {
286
+ background: #7f1d1d;
287
+ color: #fca5a5;
288
+ }
289
+
290
+ .service-status.unknown, .service-status.failed {
291
+ background: #78350f;
292
+ color: #fcd34d;
293
+ }
294
+
295
+ .btn-group {
296
+ display: flex;
297
+ gap: 10px;
298
+ flex-wrap: wrap;
299
+ }
300
+
301
+ .btn-secondary {
302
+ background: #4b5563;
303
+ color: #fff;
304
+ }
305
+
306
+ .btn-secondary:hover {
307
+ background: #374151;
308
+ }
309
+
310
+ .btn:disabled {
311
+ opacity: 0.5;
312
+ cursor: not-allowed;
313
+ }
314
+
315
+ .btn-small {
316
+ padding: 4px 10px;
317
+ font-size: 0.8rem;
318
+ }
319
+
320
+ .model-actions {
321
+ display: flex;
322
+ gap: 8px;
323
+ }
324
+
325
+ .modal {
326
+ display: none;
327
+ position: fixed;
328
+ top: 0;
329
+ left: 0;
330
+ width: 100%;
331
+ height: 100%;
332
+ background: rgba(0, 0, 0, 0.7);
333
+ justify-content: center;
334
+ align-items: center;
335
+ z-index: 2000;
336
+ }
337
+
338
+ .modal-content {
339
+ background: #1e1e2e;
340
+ border-radius: 12px;
341
+ width: 90%;
342
+ max-width: 500px;
343
+ max-height: 85vh;
344
+ overflow-y: auto;
345
+ border: 1px solid rgba(255, 255, 255, 0.1);
346
+ }
347
+
348
+ .modal-header {
349
+ display: flex;
350
+ justify-content: space-between;
351
+ align-items: center;
352
+ padding: 15px 20px;
353
+ border-bottom: 1px solid #333;
354
+ }
355
+
356
+ .modal-header h3 {
357
+ color: #00d4ff;
358
+ font-size: 1.1rem;
359
+ }
360
+
361
+ .modal-close {
362
+ font-size: 1.5rem;
363
+ color: #888;
364
+ cursor: pointer;
365
+ line-height: 1;
366
+ }
367
+
368
+ .modal-close:hover {
369
+ color: #fff;
370
+ }
371
+
372
+ .modal-body {
373
+ padding: 20px;
374
+ }
375
+
376
+ .modal-body h4 {
377
+ color: #00d4ff;
378
+ font-size: 0.95rem;
379
+ margin: 15px 0 10px;
380
+ padding-top: 10px;
381
+ border-top: 1px solid #333;
382
+ }
383
+
384
+ .modal-body h4:first-child {
385
+ margin-top: 0;
386
+ padding-top: 0;
387
+ border-top: none;
388
+ }
389
+
390
+ .modal-actions {
391
+ display: flex;
392
+ gap: 10px;
393
+ margin-top: 20px;
394
+ padding-top: 15px;
395
+ border-top: 1px solid #333;
396
+ }
397
+
398
+ .modal-actions .btn-danger {
399
+ margin-right: auto;
400
+ }
401
+
402
+ .input-with-btn {
403
+ display: flex;
404
+ gap: 8px;
405
+ }
406
+
407
+ .input-with-btn input {
408
+ flex: 1;
409
+ }
410
+
411
+ .add-model-section {
412
+ margin-top: 20px;
413
+ padding-top: 20px;
414
+ border-top: 1px solid #333;
415
+ }
416
+
417
+ .add-model-section h4 {
418
+ color: #00d4ff;
419
+ font-size: 0.95rem;
420
+ margin-bottom: 15px;
421
+ }
422
+
423
+ .half-row {
424
+ display: flex;
425
+ gap: 15px;
426
+ }
427
+
428
+ .half-row > div {
429
+ flex: 1;
430
+ }
431
+
432
+ .half-row input {
433
+ width: 100%;
434
+ }
435
+
436
+ .channel-tabs {
437
+ display: flex;
438
+ gap: 10px;
439
+ margin-bottom: 20px;
440
+ }
441
+
442
+ .tab-btn {
443
+ padding: 8px 20px;
444
+ border: 1px solid #333;
445
+ background: rgba(255, 255, 255, 0.05);
446
+ color: #aaa;
447
+ border-radius: 6px;
448
+ cursor: pointer;
449
+ transition: all 0.2s;
450
+ }
451
+
452
+ .tab-btn:hover {
453
+ background: rgba(255, 255, 255, 0.1);
454
+ }
455
+
456
+ .tab-btn.active {
457
+ background: #00d4ff;
458
+ color: #000;
459
+ border-color: #00d4ff;
460
+ }
461
+
462
+ .channel-content {
463
+ background: rgba(255, 255, 255, 0.02);
464
+ padding: 20px;
465
+ border-radius: 8px;
466
+ }
467
+
468
+ .form-row select {
469
+ width: 100%;
470
+ padding: 10px 12px;
471
+ border: 1px solid #333;
472
+ border-radius: 6px;
473
+ background: #2a2a4a;
474
+ color: #e4e4e4;
475
+ font-size: 0.95rem;
476
+ }
477
+
478
+ .form-row select:focus {
479
+ outline: none;
480
+ border-color: #00d4ff;
481
+ }
482
+
483
+ .form-row select option {
484
+ background: #1e1e2e;
485
+ color: #e4e4e4;
486
+ }
487
+
488
+ .function-output {
489
+ margin-top: 15px;
490
+ background: rgba(0, 0, 0, 0.3);
491
+ border-radius: 8px;
492
+ padding: 15px;
493
+ max-height: 300px;
494
+ overflow-y: auto;
495
+ }
496
+
497
+ .function-output pre {
498
+ font-family: 'Courier New', monospace;
499
+ font-size: 0.85rem;
500
+ white-space: pre-wrap;
501
+ word-break: break-all;
502
+ color: #4ade80;
503
+ margin-bottom: 10px;
504
+ }
505
+
506
+ .function-output .btn {
507
+ float: right;
508
+ }