codexmate 0.0.4 → 0.0.5
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/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- package/CHANGELOG.md +7 -0
- package/CHANGELOG.zh-CN.md +7 -0
- package/README.md +89 -54
- package/README.zh-CN.md +97 -58
- package/cli.js +1594 -411
- package/package.json +11 -5
- package/tests/e2e/recent-health.e2e.js +135 -0
- package/tests/e2e/run.js +294 -0
- package/web-ui.html +987 -548
package/web-ui.html
CHANGED
|
@@ -523,6 +523,101 @@
|
|
|
523
523
|
gap: var(--spacing-xs);
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
.recent-list {
|
|
527
|
+
display: flex;
|
|
528
|
+
flex-wrap: wrap;
|
|
529
|
+
gap: 8px;
|
|
530
|
+
margin-top: 8px;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.recent-item {
|
|
534
|
+
border: 1px solid var(--color-border-soft);
|
|
535
|
+
background: var(--color-surface-elevated);
|
|
536
|
+
border-radius: var(--radius-md);
|
|
537
|
+
padding: 10px 12px;
|
|
538
|
+
min-width: 170px;
|
|
539
|
+
text-align: left;
|
|
540
|
+
cursor: pointer;
|
|
541
|
+
transition: all var(--transition-fast) var(--ease-spring);
|
|
542
|
+
box-shadow: 0 2px 6px rgba(27, 23, 20, 0.06);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.recent-item:hover {
|
|
546
|
+
transform: translateY(-1px);
|
|
547
|
+
box-shadow: 0 4px 12px rgba(27, 23, 20, 0.12);
|
|
548
|
+
border-color: var(--color-border);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.recent-item:disabled {
|
|
552
|
+
opacity: 0.6;
|
|
553
|
+
cursor: not-allowed;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.recent-provider {
|
|
557
|
+
font-size: var(--font-size-body);
|
|
558
|
+
font-weight: var(--font-weight-secondary);
|
|
559
|
+
color: var(--color-text-primary);
|
|
560
|
+
margin-bottom: 4px;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.recent-model {
|
|
564
|
+
font-size: var(--font-size-caption);
|
|
565
|
+
color: var(--color-text-tertiary);
|
|
566
|
+
line-height: 1.4;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.recent-empty {
|
|
570
|
+
font-size: var(--font-size-caption);
|
|
571
|
+
color: var(--color-text-tertiary);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.health-report {
|
|
575
|
+
margin-top: 10px;
|
|
576
|
+
padding: 10px 12px;
|
|
577
|
+
border-radius: var(--radius-md);
|
|
578
|
+
border: 1px solid var(--color-border-soft);
|
|
579
|
+
background: var(--color-surface-alt);
|
|
580
|
+
display: grid;
|
|
581
|
+
gap: 8px;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.health-remote-toggle {
|
|
585
|
+
display: inline-flex;
|
|
586
|
+
align-items: center;
|
|
587
|
+
gap: 8px;
|
|
588
|
+
font-size: var(--font-size-caption);
|
|
589
|
+
color: var(--color-text-secondary);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.health-remote-toggle input {
|
|
593
|
+
accent-color: var(--color-brand);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.health-ok {
|
|
597
|
+
color: var(--color-success);
|
|
598
|
+
font-weight: var(--font-weight-secondary);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.health-issue {
|
|
602
|
+
background: #fff6f5;
|
|
603
|
+
border-left: 3px solid var(--color-error);
|
|
604
|
+
padding: 8px 10px;
|
|
605
|
+
border-radius: 10px;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.health-issue-title {
|
|
609
|
+
font-size: var(--font-size-caption);
|
|
610
|
+
font-weight: var(--font-weight-secondary);
|
|
611
|
+
color: var(--color-text-primary);
|
|
612
|
+
margin-bottom: 4px;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.health-issue-suggestion {
|
|
616
|
+
font-size: var(--font-size-caption);
|
|
617
|
+
color: var(--color-text-secondary);
|
|
618
|
+
line-height: 1.4;
|
|
619
|
+
}
|
|
620
|
+
|
|
526
621
|
.btn-icon {
|
|
527
622
|
width: 28px;
|
|
528
623
|
height: 28px;
|
|
@@ -580,6 +675,31 @@
|
|
|
580
675
|
box-shadow: var(--shadow-input-focus);
|
|
581
676
|
}
|
|
582
677
|
|
|
678
|
+
.model-input {
|
|
679
|
+
width: 100%;
|
|
680
|
+
padding: 12px var(--spacing-sm);
|
|
681
|
+
border: 1px solid var(--color-border-soft);
|
|
682
|
+
border-radius: var(--radius-sm);
|
|
683
|
+
font-size: var(--font-size-body);
|
|
684
|
+
font-weight: var(--font-weight-body);
|
|
685
|
+
background-color: var(--color-surface-alt);
|
|
686
|
+
color: var(--color-text-primary);
|
|
687
|
+
outline: none;
|
|
688
|
+
transition: all var(--transition-fast) var(--ease-smooth);
|
|
689
|
+
box-shadow: inset 0 1px 2px rgba(31, 26, 23, 0.04);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.model-input:hover {
|
|
693
|
+
border-color: var(--color-border-strong);
|
|
694
|
+
background-color: var(--color-surface);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.model-input:focus {
|
|
698
|
+
background-color: var(--color-surface);
|
|
699
|
+
border-color: var(--color-brand);
|
|
700
|
+
box-shadow: var(--shadow-input-focus);
|
|
701
|
+
}
|
|
702
|
+
|
|
583
703
|
.config-template-hint {
|
|
584
704
|
margin-top: 8px;
|
|
585
705
|
margin-bottom: 10px;
|
|
@@ -1280,6 +1400,37 @@
|
|
|
1280
1400
|
}
|
|
1281
1401
|
}
|
|
1282
1402
|
|
|
1403
|
+
@media (max-width: 520px) {
|
|
1404
|
+
.session-item-header {
|
|
1405
|
+
flex-direction: column;
|
|
1406
|
+
align-items: stretch;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
.session-item-actions {
|
|
1410
|
+
justify-content: flex-end;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
.session-actions {
|
|
1414
|
+
width: 100%;
|
|
1415
|
+
flex-direction: column;
|
|
1416
|
+
align-items: stretch;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
.btn-session-refresh,
|
|
1420
|
+
.btn-session-export {
|
|
1421
|
+
width: 100%;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
.session-toolbar-group.session-toolbar-actions {
|
|
1425
|
+
flex-direction: column;
|
|
1426
|
+
align-items: stretch;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
.session-toolbar-group.session-toolbar-actions .btn-tool {
|
|
1430
|
+
width: 100%;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1283
1434
|
.btn[disabled] {
|
|
1284
1435
|
opacity: 0.5;
|
|
1285
1436
|
cursor: not-allowed;
|
|
@@ -1666,23 +1817,23 @@
|
|
|
1666
1817
|
Codex<br>
|
|
1667
1818
|
<span class="accent">Mate.</span>
|
|
1668
1819
|
</h1>
|
|
1669
|
-
<p class="subtitle">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw / 会话。</p>
|
|
1820
|
+
<p class="subtitle">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw / 会话。</p>
|
|
1670
1821
|
|
|
1671
1822
|
<!-- 模式切换器 -->
|
|
1672
|
-
<div class="segmented-control">
|
|
1673
|
-
<button :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">
|
|
1674
|
-
Codex 配置
|
|
1675
|
-
</button>
|
|
1676
|
-
<button :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">
|
|
1677
|
-
Claude Code 配置
|
|
1678
|
-
</button>
|
|
1679
|
-
<button :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">
|
|
1680
|
-
OpenClaw 配置
|
|
1681
|
-
</button>
|
|
1682
|
-
<button :class="['segment', { active: configMode === 'sessions' }]" @click="switchConfigMode('sessions')">
|
|
1683
|
-
会话浏览
|
|
1684
|
-
</button>
|
|
1685
|
-
</div>
|
|
1823
|
+
<div class="segmented-control">
|
|
1824
|
+
<button :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">
|
|
1825
|
+
Codex 配置
|
|
1826
|
+
</button>
|
|
1827
|
+
<button :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">
|
|
1828
|
+
Claude Code 配置
|
|
1829
|
+
</button>
|
|
1830
|
+
<button :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">
|
|
1831
|
+
OpenClaw 配置
|
|
1832
|
+
</button>
|
|
1833
|
+
<button :class="['segment', { active: configMode === 'sessions' }]" @click="switchConfigMode('sessions')">
|
|
1834
|
+
会话浏览
|
|
1835
|
+
</button>
|
|
1836
|
+
</div>
|
|
1686
1837
|
|
|
1687
1838
|
<!-- 内容包裹器 - 稳定布局 -->
|
|
1688
1839
|
<div class="content-wrapper">
|
|
@@ -1701,33 +1852,107 @@
|
|
|
1701
1852
|
<div class="selector-header">
|
|
1702
1853
|
<span class="selector-title">模型</span>
|
|
1703
1854
|
<div class="selector-actions">
|
|
1704
|
-
<button class="btn-icon" @click="showModelModal = true" title="添加模型">+</button>
|
|
1705
|
-
<button class="btn-icon" @click="showModelListModal = true" title="管理模型">≡</button>
|
|
1855
|
+
<button class="btn-icon" @click="showModelModal = true" title="添加模型" v-if="modelsSource === 'legacy'">+</button>
|
|
1856
|
+
<button class="btn-icon" @click="showModelListModal = true" title="管理模型" v-if="modelsSource === 'legacy'">≡</button>
|
|
1706
1857
|
</div>
|
|
1707
1858
|
</div>
|
|
1708
|
-
<select
|
|
1709
|
-
|
|
1859
|
+
<select
|
|
1860
|
+
v-if="codexModelsLoading || modelsSource === 'remote'"
|
|
1861
|
+
class="model-select"
|
|
1862
|
+
v-model="currentModel"
|
|
1863
|
+
@change="onModelChange"
|
|
1864
|
+
:disabled="codexModelsLoading"
|
|
1865
|
+
>
|
|
1866
|
+
<option v-if="codexModelsLoading" value="">加载中...</option>
|
|
1867
|
+
<option v-else v-for="model in models" :key="model" :value="model">{{ model }}</option>
|
|
1710
1868
|
</select>
|
|
1869
|
+
<input
|
|
1870
|
+
v-if="!codexModelsLoading && (modelsSource !== 'remote' || !modelsHasCurrent)"
|
|
1871
|
+
class="model-input"
|
|
1872
|
+
v-model="currentModel"
|
|
1873
|
+
@blur="onModelChange"
|
|
1874
|
+
placeholder="例如: gpt-5.3-codex"
|
|
1875
|
+
>
|
|
1876
|
+
<div class="config-template-hint" v-if="modelsSource === 'unlimited'">
|
|
1877
|
+
当前提供商未提供模型列表,视为不限;模型可手动输入。
|
|
1878
|
+
</div>
|
|
1879
|
+
<div class="config-template-hint" v-if="modelsSource === 'error'">
|
|
1880
|
+
模型列表获取失败,请检查接口或手动输入。
|
|
1881
|
+
</div>
|
|
1882
|
+
<div class="config-template-hint" v-if="modelsSource === 'remote' && !modelsHasCurrent">
|
|
1883
|
+
当前模型不在接口列表中,请手动输入或在模板中调整。
|
|
1884
|
+
</div>
|
|
1711
1885
|
<div class="config-template-hint">
|
|
1712
|
-
Codex
|
|
1886
|
+
Codex 配置需先改模板,再手动应用。
|
|
1713
1887
|
</div>
|
|
1714
1888
|
<button class="btn-tool btn-template-editor" @click="openConfigTemplateEditor" :disabled="loading || !!initError">
|
|
1715
1889
|
打开 Config 模板编辑器
|
|
1716
1890
|
</button>
|
|
1717
1891
|
</div>
|
|
1718
1892
|
|
|
1893
|
+
<div class="selector-section">
|
|
1894
|
+
<div class="selector-header">
|
|
1895
|
+
<span class="selector-title">最近使用</span>
|
|
1896
|
+
<span v-if="recentLoading" class="selector-title">加载中...</span>
|
|
1897
|
+
</div>
|
|
1898
|
+
<div v-if="recentConfigs.length === 0" class="recent-empty">
|
|
1899
|
+
暂无记录
|
|
1900
|
+
</div>
|
|
1901
|
+
<div v-else class="recent-list">
|
|
1902
|
+
<button
|
|
1903
|
+
v-for="item in recentConfigs"
|
|
1904
|
+
:key="item.provider + '::' + item.model + '::' + (item.usedAt || '')"
|
|
1905
|
+
class="recent-item"
|
|
1906
|
+
@click="applyRecentConfig(item)"
|
|
1907
|
+
:disabled="loading || !!initError">
|
|
1908
|
+
<div class="recent-provider">{{ item.provider }}</div>
|
|
1909
|
+
<div class="recent-model">{{ item.model }}</div>
|
|
1910
|
+
</button>
|
|
1911
|
+
</div>
|
|
1912
|
+
</div>
|
|
1913
|
+
|
|
1719
1914
|
<div class="selector-section">
|
|
1720
1915
|
<div class="selector-header">
|
|
1721
1916
|
<span class="selector-title">AGENTS.md</span>
|
|
1722
1917
|
</div>
|
|
1723
1918
|
<div class="config-template-hint">
|
|
1724
|
-
|
|
1919
|
+
Codex 指令:<code>~/.codex/AGENTS.md</code>(同级 <code>config.toml</code>)。
|
|
1725
1920
|
</div>
|
|
1726
1921
|
<button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
1727
1922
|
{{ agentsLoading ? '加载中...' : '打开 AGENTS.md 编辑器' }}
|
|
1728
1923
|
</button>
|
|
1729
1924
|
</div>
|
|
1730
1925
|
|
|
1926
|
+
<div class="selector-section">
|
|
1927
|
+
<div class="selector-header">
|
|
1928
|
+
<span class="selector-title">配置健康检查</span>
|
|
1929
|
+
</div>
|
|
1930
|
+
<div class="config-template-hint">
|
|
1931
|
+
检测 base_url、API Key 与模型可用性(可选远程探测会发起真实请求,可能产生费用)。
|
|
1932
|
+
</div>
|
|
1933
|
+
<div class="config-template-hint">
|
|
1934
|
+
<label class="health-remote-toggle">
|
|
1935
|
+
<input type="checkbox" v-model="healthCheckRemote">
|
|
1936
|
+
启用远程探测(请求 base_url 与 /v1/models)
|
|
1937
|
+
</label>
|
|
1938
|
+
</div>
|
|
1939
|
+
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
1940
|
+
{{ healthCheckLoading ? '检查中...' : '运行检查' }}
|
|
1941
|
+
</button>
|
|
1942
|
+
<div v-if="healthCheckResult" class="health-report">
|
|
1943
|
+
<div v-if="healthCheckResult.ok" class="health-ok">未发现问题</div>
|
|
1944
|
+
<div v-else>
|
|
1945
|
+
<div
|
|
1946
|
+
v-for="(issue, index) in healthCheckResult.issues"
|
|
1947
|
+
:key="(issue.code || 'issue') + index"
|
|
1948
|
+
class="health-issue">
|
|
1949
|
+
<div class="health-issue-title">{{ issue.message }}</div>
|
|
1950
|
+
<div v-if="issue.suggestion" class="health-issue-suggestion">{{ issue.suggestion }}</div>
|
|
1951
|
+
</div>
|
|
1952
|
+
</div>
|
|
1953
|
+
</div>
|
|
1954
|
+
</div>
|
|
1955
|
+
|
|
1731
1956
|
<div v-if="!loading && !initError" class="card-list">
|
|
1732
1957
|
<div v-for="provider in providersList" :key="provider.name"
|
|
1733
1958
|
:class="['card', { active: currentProvider === provider.name }]"
|
|
@@ -1770,17 +1995,33 @@
|
|
|
1770
1995
|
</div>
|
|
1771
1996
|
</div>
|
|
1772
1997
|
|
|
1773
|
-
<!-- Claude Code 配置模式 -->
|
|
1774
|
-
<div v-show="configMode === 'claude'" class="mode-content">
|
|
1775
|
-
<!-- 添加提供商按钮 -->
|
|
1776
|
-
<button class="btn-add" @click="
|
|
1998
|
+
<!-- Claude Code 配置模式 -->
|
|
1999
|
+
<div v-show="configMode === 'claude'" class="mode-content">
|
|
2000
|
+
<!-- 添加提供商按钮 -->
|
|
2001
|
+
<button class="btn-add" @click="openClaudeConfigModal" v-if="!loading && !initError">
|
|
1777
2002
|
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
1778
2003
|
<path d="M10 4v12M4 10h12"/>
|
|
1779
2004
|
</svg>
|
|
1780
2005
|
添加提供商
|
|
1781
2006
|
</button>
|
|
1782
2007
|
<div class="config-template-hint">
|
|
1783
|
-
默认应用到 <code>~/.claude/settings.json</code
|
|
2008
|
+
默认应用到 <code>~/.claude/settings.json</code>。
|
|
2009
|
+
</div>
|
|
2010
|
+
|
|
2011
|
+
<div class="selector-section">
|
|
2012
|
+
<div class="selector-header">
|
|
2013
|
+
<span class="selector-title">模型</span>
|
|
2014
|
+
</div>
|
|
2015
|
+
<input
|
|
2016
|
+
class="model-input"
|
|
2017
|
+
v-model="currentClaudeModel"
|
|
2018
|
+
@blur="onClaudeModelChange"
|
|
2019
|
+
@keyup.enter="onClaudeModelChange"
|
|
2020
|
+
placeholder="例如: claude-3-7-sonnet"
|
|
2021
|
+
>
|
|
2022
|
+
<div class="config-template-hint">
|
|
2023
|
+
模型修改后会自动保存并应用到当前配置。
|
|
2024
|
+
</div>
|
|
1784
2025
|
</div>
|
|
1785
2026
|
|
|
1786
2027
|
<div class="card-list">
|
|
@@ -1814,69 +2055,69 @@
|
|
|
1814
2055
|
</div>
|
|
1815
2056
|
</div>
|
|
1816
2057
|
</div>
|
|
1817
|
-
</div>
|
|
1818
|
-
</div>
|
|
1819
|
-
|
|
1820
|
-
<!-- OpenClaw 配置模式 -->
|
|
1821
|
-
<div v-show="configMode === 'openclaw'" class="mode-content">
|
|
1822
|
-
<button class="btn-add" @click="openOpenclawAddModal" v-if="!loading && !initError">
|
|
1823
|
-
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
1824
|
-
<path d="M10 4v12M4 10h12"/>
|
|
1825
|
-
</svg>
|
|
1826
|
-
添加 OpenClaw 配置
|
|
1827
|
-
</button>
|
|
1828
|
-
<div class="config-template-hint">
|
|
1829
|
-
默认应用到 <code>~/.openclaw/openclaw.json</code>;支持 JSON5(注释/尾逗号)。
|
|
1830
|
-
</div>
|
|
1831
|
-
|
|
1832
|
-
<div class="selector-section">
|
|
1833
|
-
<div class="selector-header">
|
|
1834
|
-
<span class="selector-title">AGENTS.md</span>
|
|
1835
|
-
</div>
|
|
1836
|
-
<div class="config-template-hint">
|
|
1837
|
-
管理 OpenClaw Workspace 指令文件,默认读写 <code>~/.openclaw/workspace/AGENTS.md</code>。
|
|
1838
|
-
</div>
|
|
1839
|
-
<button class="btn-tool" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
1840
|
-
{{ agentsLoading ? '加载中...' : '打开 AGENTS.md 编辑器' }}
|
|
1841
|
-
</button>
|
|
1842
|
-
</div>
|
|
1843
|
-
|
|
1844
|
-
<div class="card-list">
|
|
1845
|
-
<div v-for="(config, name) in openclawConfigs" :key="name"
|
|
1846
|
-
:class="['card', { active: currentOpenclawConfig === name }]"
|
|
1847
|
-
@click="applyOpenclawConfig(name)">
|
|
1848
|
-
<div class="card-leading">
|
|
1849
|
-
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
|
|
1850
|
-
<div class="card-content">
|
|
1851
|
-
<div class="card-title">{{ name }}</div>
|
|
1852
|
-
<div class="card-subtitle">{{ openclawSubtitle(config) }}</div>
|
|
1853
|
-
</div>
|
|
1854
|
-
</div>
|
|
1855
|
-
<div class="card-trailing">
|
|
1856
|
-
<span :class="['pill', openclawHasContent(config) ? 'configured' : 'empty']">
|
|
1857
|
-
{{ openclawHasContent(config) ? '已配置' : '未配置' }}
|
|
1858
|
-
</span>
|
|
1859
|
-
<div class="card-actions" @click.stop>
|
|
1860
|
-
<button class="card-action-btn" @click="openOpenclawEditModal(name)" title="编辑">
|
|
1861
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1862
|
-
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
1863
|
-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
1864
|
-
</svg>
|
|
1865
|
-
</button>
|
|
1866
|
-
<button class="card-action-btn delete" @click="deleteOpenclawConfig(name)" title="删除">
|
|
1867
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1868
|
-
<path d="M3 6h18"/>
|
|
1869
|
-
<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"/>
|
|
1870
|
-
</svg>
|
|
1871
|
-
</button>
|
|
1872
|
-
</div>
|
|
1873
|
-
</div>
|
|
1874
|
-
</div>
|
|
1875
|
-
</div>
|
|
1876
|
-
</div>
|
|
1877
|
-
|
|
1878
|
-
<!-- 会话浏览模式 -->
|
|
1879
|
-
<div v-show="configMode === 'sessions'" class="mode-content">
|
|
2058
|
+
</div>
|
|
2059
|
+
</div>
|
|
2060
|
+
|
|
2061
|
+
<!-- OpenClaw 配置模式 -->
|
|
2062
|
+
<div v-show="configMode === 'openclaw'" class="mode-content">
|
|
2063
|
+
<button class="btn-add" @click="openOpenclawAddModal" v-if="!loading && !initError">
|
|
2064
|
+
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
2065
|
+
<path d="M10 4v12M4 10h12"/>
|
|
2066
|
+
</svg>
|
|
2067
|
+
添加 OpenClaw 配置
|
|
2068
|
+
</button>
|
|
2069
|
+
<div class="config-template-hint">
|
|
2070
|
+
默认应用到 <code>~/.openclaw/openclaw.json</code>;支持 JSON5(注释/尾逗号)。
|
|
2071
|
+
</div>
|
|
2072
|
+
|
|
2073
|
+
<div class="selector-section">
|
|
2074
|
+
<div class="selector-header">
|
|
2075
|
+
<span class="selector-title">AGENTS.md</span>
|
|
2076
|
+
</div>
|
|
2077
|
+
<div class="config-template-hint">
|
|
2078
|
+
管理 OpenClaw Workspace 指令文件,默认读写 <code>~/.openclaw/workspace/AGENTS.md</code>。
|
|
2079
|
+
</div>
|
|
2080
|
+
<button class="btn-tool" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
2081
|
+
{{ agentsLoading ? '加载中...' : '打开 AGENTS.md 编辑器' }}
|
|
2082
|
+
</button>
|
|
2083
|
+
</div>
|
|
2084
|
+
|
|
2085
|
+
<div class="card-list">
|
|
2086
|
+
<div v-for="(config, name) in openclawConfigs" :key="name"
|
|
2087
|
+
:class="['card', { active: currentOpenclawConfig === name }]"
|
|
2088
|
+
@click="applyOpenclawConfig(name)">
|
|
2089
|
+
<div class="card-leading">
|
|
2090
|
+
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
|
|
2091
|
+
<div class="card-content">
|
|
2092
|
+
<div class="card-title">{{ name }}</div>
|
|
2093
|
+
<div class="card-subtitle">{{ openclawSubtitle(config) }}</div>
|
|
2094
|
+
</div>
|
|
2095
|
+
</div>
|
|
2096
|
+
<div class="card-trailing">
|
|
2097
|
+
<span :class="['pill', openclawHasContent(config) ? 'configured' : 'empty']">
|
|
2098
|
+
{{ openclawHasContent(config) ? '已配置' : '未配置' }}
|
|
2099
|
+
</span>
|
|
2100
|
+
<div class="card-actions" @click.stop>
|
|
2101
|
+
<button class="card-action-btn" @click="openOpenclawEditModal(name)" title="编辑">
|
|
2102
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2103
|
+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
2104
|
+
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
2105
|
+
</svg>
|
|
2106
|
+
</button>
|
|
2107
|
+
<button class="card-action-btn delete" @click="deleteOpenclawConfig(name)" title="删除">
|
|
2108
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2109
|
+
<path d="M3 6h18"/>
|
|
2110
|
+
<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"/>
|
|
2111
|
+
</svg>
|
|
2112
|
+
</button>
|
|
2113
|
+
</div>
|
|
2114
|
+
</div>
|
|
2115
|
+
</div>
|
|
2116
|
+
</div>
|
|
2117
|
+
</div>
|
|
2118
|
+
|
|
2119
|
+
<!-- 会话浏览模式 -->
|
|
2120
|
+
<div v-show="configMode === 'sessions'" class="mode-content">
|
|
1880
2121
|
<div class="selector-section">
|
|
1881
2122
|
<div class="selector-header">
|
|
1882
2123
|
<span class="selector-title">会话来源</span>
|
|
@@ -1937,7 +2178,8 @@
|
|
|
1937
2178
|
</div>
|
|
1938
2179
|
</div>
|
|
1939
2180
|
<div class="session-hint">
|
|
1940
|
-
|
|
2181
|
+
关键词/角色/时间筛选暂不可用;<br>
|
|
2182
|
+
仅支持来源与路径筛选,右侧仅查看/导出。
|
|
1941
2183
|
</div>
|
|
1942
2184
|
</div>
|
|
1943
2185
|
|
|
@@ -2172,10 +2414,6 @@
|
|
|
2172
2414
|
<label class="form-label">Base URL</label>
|
|
2173
2415
|
<input v-model="newClaudeConfig.baseUrl" class="form-input" placeholder="https://open.bigmodel.cn/api/anthropic">
|
|
2174
2416
|
</div>
|
|
2175
|
-
<div class="form-group">
|
|
2176
|
-
<label class="form-label">模型</label>
|
|
2177
|
-
<input v-model="newClaudeConfig.model" class="form-input" placeholder="例如: claude-sonnet-4-20250514">
|
|
2178
|
-
</div>
|
|
2179
2417
|
|
|
2180
2418
|
<div class="btn-group">
|
|
2181
2419
|
<button class="btn btn-cancel" @click="closeClaudeConfigModal">取消</button>
|
|
@@ -2185,9 +2423,9 @@
|
|
|
2185
2423
|
</div>
|
|
2186
2424
|
|
|
2187
2425
|
<!-- 编辑Claude配置模态框 -->
|
|
2188
|
-
<div v-if="showEditConfigModal" class="modal-overlay" @click.self="closeEditConfigModal">
|
|
2189
|
-
<div class="modal">
|
|
2190
|
-
<div class="modal-title">编辑 Claude Code 配置</div>
|
|
2426
|
+
<div v-if="showEditConfigModal" class="modal-overlay" @click.self="closeEditConfigModal">
|
|
2427
|
+
<div class="modal">
|
|
2428
|
+
<div class="modal-title">编辑 Claude Code 配置</div>
|
|
2191
2429
|
|
|
2192
2430
|
<div class="form-group">
|
|
2193
2431
|
<label class="form-label">配置名称</label>
|
|
@@ -2201,71 +2439,65 @@
|
|
|
2201
2439
|
<label class="form-label">Base URL</label>
|
|
2202
2440
|
<input v-model="editingConfig.baseUrl" class="form-input" placeholder="https://open.bigmodel.cn/api/anthropic">
|
|
2203
2441
|
</div>
|
|
2442
|
+
|
|
2443
|
+
<div class="btn-group">
|
|
2444
|
+
<button class="btn btn-cancel" @click="closeEditConfigModal">取消</button>
|
|
2445
|
+
<button class="btn btn-confirm" @click="saveAndApplyConfig">保存并应用</button>
|
|
2446
|
+
</div>
|
|
2447
|
+
</div>
|
|
2448
|
+
</div>
|
|
2449
|
+
|
|
2450
|
+
<div v-if="showOpenclawConfigModal" class="modal-overlay" @click.self="closeOpenclawConfigModal">
|
|
2451
|
+
<div class="modal modal-wide">
|
|
2452
|
+
<div class="modal-title">{{ openclawEditorTitle }}</div>
|
|
2453
|
+
|
|
2204
2454
|
<div class="form-group">
|
|
2205
|
-
<label class="form-label"
|
|
2206
|
-
<input v-model="
|
|
2455
|
+
<label class="form-label">配置名称</label>
|
|
2456
|
+
<input v-model="openclawEditing.name" class="form-input" :readonly="openclawEditing.lockName" placeholder="例如: 默认配置">
|
|
2457
|
+
</div>
|
|
2458
|
+
|
|
2459
|
+
<div class="form-group">
|
|
2460
|
+
<label class="form-label">目标文件</label>
|
|
2461
|
+
<div class="form-hint">
|
|
2462
|
+
{{ openclawConfigPath || '未加载' }}
|
|
2463
|
+
<span v-if="openclawConfigPath">
|
|
2464
|
+
({{ openclawConfigExists ? '已存在' : '不存在,将在应用时创建' }})
|
|
2465
|
+
</span>
|
|
2466
|
+
</div>
|
|
2467
|
+
<div class="btn-group" style="justify-content:flex-start;">
|
|
2468
|
+
<button class="btn btn-confirm secondary" @click="loadOpenclawConfigFromFile" :disabled="openclawFileLoading">
|
|
2469
|
+
{{ openclawFileLoading ? '加载中...' : '加载当前配置' }}
|
|
2470
|
+
</button>
|
|
2471
|
+
</div>
|
|
2472
|
+
</div>
|
|
2473
|
+
|
|
2474
|
+
<div class="form-group">
|
|
2475
|
+
<label class="form-label">OpenClaw 配置(JSON5)</label>
|
|
2476
|
+
<textarea
|
|
2477
|
+
v-model="openclawEditing.content"
|
|
2478
|
+
class="form-input template-editor"
|
|
2479
|
+
spellcheck="false"
|
|
2480
|
+
placeholder="在这里编辑 OpenClaw 配置(JSON5)"></textarea>
|
|
2481
|
+
<div class="template-editor-warning">
|
|
2482
|
+
保存仅写入本地配置库;点击“保存并应用”后会写入 openclaw.json。
|
|
2483
|
+
</div>
|
|
2207
2484
|
</div>
|
|
2208
2485
|
|
|
2209
2486
|
<div class="btn-group">
|
|
2210
|
-
<button class="btn btn-cancel" @click="
|
|
2211
|
-
<button class="btn btn-confirm" @click="
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
<input v-model="openclawEditing.name" class="form-input" :readonly="openclawEditing.lockName" placeholder="例如: 默认配置">
|
|
2225
|
-
</div>
|
|
2226
|
-
|
|
2227
|
-
<div class="form-group">
|
|
2228
|
-
<label class="form-label">目标文件</label>
|
|
2229
|
-
<div class="form-hint">
|
|
2230
|
-
{{ openclawConfigPath || '未加载' }}
|
|
2231
|
-
<span v-if="openclawConfigPath">
|
|
2232
|
-
({{ openclawConfigExists ? '已存在' : '不存在,将在应用时创建' }})
|
|
2233
|
-
</span>
|
|
2234
|
-
</div>
|
|
2235
|
-
<div class="btn-group" style="justify-content:flex-start;">
|
|
2236
|
-
<button class="btn btn-confirm secondary" @click="loadOpenclawConfigFromFile" :disabled="openclawFileLoading">
|
|
2237
|
-
{{ openclawFileLoading ? '加载中...' : '加载当前配置' }}
|
|
2238
|
-
</button>
|
|
2239
|
-
</div>
|
|
2240
|
-
</div>
|
|
2241
|
-
|
|
2242
|
-
<div class="form-group">
|
|
2243
|
-
<label class="form-label">OpenClaw 配置(JSON5)</label>
|
|
2244
|
-
<textarea
|
|
2245
|
-
v-model="openclawEditing.content"
|
|
2246
|
-
class="form-input template-editor"
|
|
2247
|
-
spellcheck="false"
|
|
2248
|
-
placeholder="在这里编辑 OpenClaw 配置(JSON5)"></textarea>
|
|
2249
|
-
<div class="template-editor-warning">
|
|
2250
|
-
保存仅写入本地配置库;点击“保存并应用”后会写入 openclaw.json。
|
|
2251
|
-
</div>
|
|
2252
|
-
</div>
|
|
2253
|
-
|
|
2254
|
-
<div class="btn-group">
|
|
2255
|
-
<button class="btn btn-cancel" @click="closeOpenclawConfigModal">取消</button>
|
|
2256
|
-
<button class="btn btn-confirm" @click="saveOpenclawConfig" :disabled="openclawSaving">
|
|
2257
|
-
{{ openclawSaving ? '保存中...' : '保存' }}
|
|
2258
|
-
</button>
|
|
2259
|
-
<button class="btn btn-confirm secondary" @click="saveAndApplyOpenclawConfig" :disabled="openclawApplying">
|
|
2260
|
-
{{ openclawApplying ? '应用中...' : '保存并应用' }}
|
|
2261
|
-
</button>
|
|
2262
|
-
</div>
|
|
2263
|
-
</div>
|
|
2264
|
-
</div>
|
|
2265
|
-
|
|
2266
|
-
<div v-if="showConfigTemplateModal" class="modal-overlay" @click.self="closeConfigTemplateModal">
|
|
2267
|
-
<div class="modal modal-wide">
|
|
2268
|
-
<div class="modal-title">Config 模板编辑器(手动确认应用)</div>
|
|
2487
|
+
<button class="btn btn-cancel" @click="closeOpenclawConfigModal">取消</button>
|
|
2488
|
+
<button class="btn btn-confirm" @click="saveOpenclawConfig" :disabled="openclawSaving">
|
|
2489
|
+
{{ openclawSaving ? '保存中...' : '保存' }}
|
|
2490
|
+
</button>
|
|
2491
|
+
<button class="btn btn-confirm secondary" @click="saveAndApplyOpenclawConfig" :disabled="openclawApplying">
|
|
2492
|
+
{{ openclawApplying ? '应用中...' : '保存并应用' }}
|
|
2493
|
+
</button>
|
|
2494
|
+
</div>
|
|
2495
|
+
</div>
|
|
2496
|
+
</div>
|
|
2497
|
+
|
|
2498
|
+
<div v-if="showConfigTemplateModal" class="modal-overlay" @click.self="closeConfigTemplateModal">
|
|
2499
|
+
<div class="modal modal-wide">
|
|
2500
|
+
<div class="modal-title">Config 模板编辑器(手动确认应用)</div>
|
|
2269
2501
|
|
|
2270
2502
|
<div class="form-group">
|
|
2271
2503
|
<label class="form-label">config.toml 模板</label>
|
|
@@ -2288,9 +2520,9 @@
|
|
|
2288
2520
|
</div>
|
|
2289
2521
|
</div>
|
|
2290
2522
|
|
|
2291
|
-
<div v-if="showAgentsModal" class="modal-overlay" @click.self="closeAgentsModal">
|
|
2292
|
-
<div class="modal modal-wide">
|
|
2293
|
-
<div class="modal-title">{{ agentsModalTitle }}</div>
|
|
2523
|
+
<div v-if="showAgentsModal" class="modal-overlay" @click.self="closeAgentsModal">
|
|
2524
|
+
<div class="modal modal-wide">
|
|
2525
|
+
<div class="modal-title">{{ agentsModalTitle }}</div>
|
|
2294
2526
|
|
|
2295
2527
|
<div class="form-group">
|
|
2296
2528
|
<label class="form-label">目标文件</label>
|
|
@@ -2304,16 +2536,16 @@
|
|
|
2304
2536
|
|
|
2305
2537
|
<div class="form-group">
|
|
2306
2538
|
<label class="form-label">AGENTS.md 内容</label>
|
|
2307
|
-
<textarea
|
|
2308
|
-
v-model="agentsContent"
|
|
2309
|
-
class="form-input template-editor"
|
|
2310
|
-
spellcheck="false"
|
|
2311
|
-
:readonly="agentsLoading"
|
|
2312
|
-
placeholder="在这里编辑 AGENTS.md 内容"></textarea>
|
|
2313
|
-
<div class="template-editor-warning">
|
|
2314
|
-
{{ agentsModalHint }}
|
|
2315
|
-
</div>
|
|
2316
|
-
</div>
|
|
2539
|
+
<textarea
|
|
2540
|
+
v-model="agentsContent"
|
|
2541
|
+
class="form-input template-editor"
|
|
2542
|
+
spellcheck="false"
|
|
2543
|
+
:readonly="agentsLoading"
|
|
2544
|
+
placeholder="在这里编辑 AGENTS.md 内容"></textarea>
|
|
2545
|
+
<div class="template-editor-warning">
|
|
2546
|
+
{{ agentsModalHint }}
|
|
2547
|
+
</div>
|
|
2548
|
+
</div>
|
|
2317
2549
|
|
|
2318
2550
|
<div class="btn-group">
|
|
2319
2551
|
<button class="btn btn-cancel" @click="closeAgentsModal">取消</button>
|
|
@@ -2330,22 +2562,22 @@
|
|
|
2330
2562
|
<div v-if="message" :class="['toast', messageType]">{{ message }}</div>
|
|
2331
2563
|
</div>
|
|
2332
2564
|
|
|
2333
|
-
<script>
|
|
2334
|
-
const { createApp } = Vue;
|
|
2335
|
-
const API_BASE = 'http://localhost:3737';
|
|
2336
|
-
const DEFAULT_OPENCLAW_TEMPLATE = `{
|
|
2337
|
-
// OpenClaw config (JSON5)
|
|
2338
|
-
agent: {
|
|
2339
|
-
model: "gpt-4.1"
|
|
2340
|
-
},
|
|
2341
|
-
agents: {
|
|
2342
|
-
defaults: {
|
|
2343
|
-
workspace: "~/.openclaw/workspace"
|
|
2344
|
-
}
|
|
2345
|
-
}
|
|
2346
|
-
}`;
|
|
2347
|
-
|
|
2348
|
-
async function api(action, params = {}) {
|
|
2565
|
+
<script>
|
|
2566
|
+
const { createApp } = Vue;
|
|
2567
|
+
const API_BASE = 'http://localhost:3737';
|
|
2568
|
+
const DEFAULT_OPENCLAW_TEMPLATE = `{
|
|
2569
|
+
// OpenClaw config (JSON5)
|
|
2570
|
+
agent: {
|
|
2571
|
+
model: "gpt-4.1"
|
|
2572
|
+
},
|
|
2573
|
+
agents: {
|
|
2574
|
+
defaults: {
|
|
2575
|
+
workspace: "~/.openclaw/workspace"
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}`;
|
|
2579
|
+
|
|
2580
|
+
async function api(action, params = {}) {
|
|
2349
2581
|
const res = await fetch(`${API_BASE}/api`, {
|
|
2350
2582
|
method: 'POST',
|
|
2351
2583
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -2362,6 +2594,13 @@
|
|
|
2362
2594
|
currentModel: '',
|
|
2363
2595
|
providersList: [],
|
|
2364
2596
|
models: [],
|
|
2597
|
+
codexModelsLoading: false,
|
|
2598
|
+
modelsSource: 'remote',
|
|
2599
|
+
modelsHasCurrent: true,
|
|
2600
|
+
claudeModels: [],
|
|
2601
|
+
claudeModelsSource: 'idle',
|
|
2602
|
+
claudeModelsHasCurrent: true,
|
|
2603
|
+
claudeModelsLoading: false,
|
|
2365
2604
|
loading: true,
|
|
2366
2605
|
initError: '',
|
|
2367
2606
|
message: '',
|
|
@@ -2370,23 +2609,23 @@
|
|
|
2370
2609
|
showEditModal: false,
|
|
2371
2610
|
showModelModal: false,
|
|
2372
2611
|
showModelListModal: false,
|
|
2373
|
-
showClaudeConfigModal: false,
|
|
2374
|
-
showEditConfigModal: false,
|
|
2375
|
-
showOpenclawConfigModal: false,
|
|
2376
|
-
showConfigTemplateModal: false,
|
|
2377
|
-
showAgentsModal: false,
|
|
2378
|
-
configTemplateContent: '',
|
|
2379
|
-
configTemplateApplying: false,
|
|
2380
|
-
agentsContent: '',
|
|
2381
|
-
agentsPath: '',
|
|
2382
|
-
agentsExists: false,
|
|
2383
|
-
agentsLineEnding: '\n',
|
|
2384
|
-
agentsLoading: false,
|
|
2385
|
-
agentsSaving: false,
|
|
2386
|
-
agentsContext: 'codex',
|
|
2387
|
-
agentsModalTitle: 'AGENTS.md 编辑器',
|
|
2388
|
-
agentsModalHint: '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
|
|
2389
|
-
sessionsList: [],
|
|
2612
|
+
showClaudeConfigModal: false,
|
|
2613
|
+
showEditConfigModal: false,
|
|
2614
|
+
showOpenclawConfigModal: false,
|
|
2615
|
+
showConfigTemplateModal: false,
|
|
2616
|
+
showAgentsModal: false,
|
|
2617
|
+
configTemplateContent: '',
|
|
2618
|
+
configTemplateApplying: false,
|
|
2619
|
+
agentsContent: '',
|
|
2620
|
+
agentsPath: '',
|
|
2621
|
+
agentsExists: false,
|
|
2622
|
+
agentsLineEnding: '\n',
|
|
2623
|
+
agentsLoading: false,
|
|
2624
|
+
agentsSaving: false,
|
|
2625
|
+
agentsContext: 'codex',
|
|
2626
|
+
agentsModalTitle: 'AGENTS.md 编辑器',
|
|
2627
|
+
agentsModalHint: '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
|
|
2628
|
+
sessionsList: [],
|
|
2390
2629
|
sessionsLoading: false,
|
|
2391
2630
|
sessionFilterSource: 'all',
|
|
2392
2631
|
sessionPathFilter: '',
|
|
@@ -2417,10 +2656,11 @@
|
|
|
2417
2656
|
speedLoading: {},
|
|
2418
2657
|
newProvider: { name: '', url: '', key: '' },
|
|
2419
2658
|
editingProvider: { name: '', url: '', key: '' },
|
|
2420
|
-
newModelName: '',
|
|
2421
|
-
currentClaudeConfig: '',
|
|
2422
|
-
|
|
2423
|
-
|
|
2659
|
+
newModelName: '',
|
|
2660
|
+
currentClaudeConfig: '',
|
|
2661
|
+
currentClaudeModel: '',
|
|
2662
|
+
editingConfig: { name: '', apiKey: '', baseUrl: '', model: '' },
|
|
2663
|
+
claudeConfigs: {
|
|
2424
2664
|
'智谱GLM': {
|
|
2425
2665
|
apiKey: '',
|
|
2426
2666
|
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
@@ -2428,33 +2668,38 @@
|
|
|
2428
2668
|
hasKey: false
|
|
2429
2669
|
}
|
|
2430
2670
|
},
|
|
2431
|
-
newClaudeConfig: {
|
|
2432
|
-
name: '',
|
|
2433
|
-
apiKey: '',
|
|
2434
|
-
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
2435
|
-
model: 'glm-4.7'
|
|
2436
|
-
},
|
|
2437
|
-
currentOpenclawConfig: '',
|
|
2438
|
-
openclawConfigs: {
|
|
2439
|
-
'默认配置': {
|
|
2440
|
-
content: DEFAULT_OPENCLAW_TEMPLATE
|
|
2441
|
-
}
|
|
2442
|
-
},
|
|
2443
|
-
openclawEditing: { name: '', content: '', lockName: false },
|
|
2444
|
-
openclawEditorTitle: '添加 OpenClaw 配置',
|
|
2445
|
-
openclawConfigPath: '',
|
|
2446
|
-
openclawConfigExists: false,
|
|
2447
|
-
openclawLineEnding: '\n',
|
|
2448
|
-
openclawFileLoading: false,
|
|
2449
|
-
openclawSaving: false,
|
|
2450
|
-
openclawApplying: false
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2671
|
+
newClaudeConfig: {
|
|
2672
|
+
name: '',
|
|
2673
|
+
apiKey: '',
|
|
2674
|
+
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
2675
|
+
model: 'glm-4.7'
|
|
2676
|
+
},
|
|
2677
|
+
currentOpenclawConfig: '',
|
|
2678
|
+
openclawConfigs: {
|
|
2679
|
+
'默认配置': {
|
|
2680
|
+
content: DEFAULT_OPENCLAW_TEMPLATE
|
|
2681
|
+
}
|
|
2682
|
+
},
|
|
2683
|
+
openclawEditing: { name: '', content: '', lockName: false },
|
|
2684
|
+
openclawEditorTitle: '添加 OpenClaw 配置',
|
|
2685
|
+
openclawConfigPath: '',
|
|
2686
|
+
openclawConfigExists: false,
|
|
2687
|
+
openclawLineEnding: '\n',
|
|
2688
|
+
openclawFileLoading: false,
|
|
2689
|
+
openclawSaving: false,
|
|
2690
|
+
openclawApplying: false,
|
|
2691
|
+
recentConfigs: [],
|
|
2692
|
+
recentLoading: false,
|
|
2693
|
+
healthCheckLoading: false,
|
|
2694
|
+
healthCheckResult: null,
|
|
2695
|
+
healthCheckRemote: false
|
|
2696
|
+
}
|
|
2697
|
+
},
|
|
2698
|
+
mounted() {
|
|
2699
|
+
const savedConfigs = localStorage.getItem('claudeConfigs');
|
|
2700
|
+
if (savedConfigs) {
|
|
2701
|
+
try {
|
|
2702
|
+
this.claudeConfigs = JSON.parse(savedConfigs);
|
|
2458
2703
|
for (const [name, config] of Object.entries(this.claudeConfigs)) {
|
|
2459
2704
|
if (config.apiKey && config.apiKey.includes('****')) {
|
|
2460
2705
|
config.apiKey = '';
|
|
@@ -2469,37 +2714,41 @@
|
|
|
2469
2714
|
}
|
|
2470
2715
|
} catch (e) {
|
|
2471
2716
|
console.error('加载 Claude 配置失败:', e);
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
this.
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
if (!this.currentClaudeConfig) {
|
|
2720
|
+
const configNames = Object.keys(this.claudeConfigs);
|
|
2721
|
+
if (configNames.length > 0) {
|
|
2722
|
+
this.currentClaudeConfig = configNames[0];
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
this.syncClaudeModelFromConfig();
|
|
2726
|
+
const savedOpenclawConfigs = localStorage.getItem('openclawConfigs');
|
|
2727
|
+
if (savedOpenclawConfigs) {
|
|
2728
|
+
try {
|
|
2729
|
+
this.openclawConfigs = JSON.parse(savedOpenclawConfigs);
|
|
2730
|
+
const configNames = Object.keys(this.openclawConfigs);
|
|
2731
|
+
if (configNames.length > 0) {
|
|
2732
|
+
this.currentOpenclawConfig = configNames[0];
|
|
2733
|
+
}
|
|
2734
|
+
} catch (e) {
|
|
2735
|
+
console.error('加载 OpenClaw 配置失败:', e);
|
|
2736
|
+
}
|
|
2737
|
+
} else {
|
|
2738
|
+
const configNames = Object.keys(this.openclawConfigs);
|
|
2739
|
+
if (configNames.length > 0) {
|
|
2740
|
+
this.currentOpenclawConfig = configNames[0];
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
this.loadAll();
|
|
2744
|
+
},
|
|
2493
2745
|
methods: {
|
|
2494
2746
|
async loadAll() {
|
|
2495
2747
|
this.loading = true;
|
|
2496
2748
|
this.initError = '';
|
|
2497
2749
|
try {
|
|
2498
|
-
const
|
|
2499
|
-
|
|
2500
|
-
api('list'),
|
|
2501
|
-
api('models')
|
|
2502
|
-
]);
|
|
2750
|
+
const statusRes = await api('status');
|
|
2751
|
+
const listRes = await api('list');
|
|
2503
2752
|
|
|
2504
2753
|
if (statusRes.error) {
|
|
2505
2754
|
this.initError = statusRes.error;
|
|
@@ -2507,14 +2756,16 @@
|
|
|
2507
2756
|
this.currentProvider = statusRes.provider;
|
|
2508
2757
|
this.currentModel = statusRes.model;
|
|
2509
2758
|
this.providersList = listRes.providers;
|
|
2510
|
-
this.
|
|
2759
|
+
await this.loadModelsForProvider(this.currentProvider);
|
|
2511
2760
|
if (statusRes.configReady === false) {
|
|
2512
2761
|
this.showMessage(statusRes.configNotice || '未检测到 config.toml,已加载默认模板;请在模板编辑器确认后创建。', 'info');
|
|
2513
2762
|
}
|
|
2514
2763
|
if (statusRes.initNotice) {
|
|
2515
2764
|
this.showMessage(statusRes.initNotice, 'info');
|
|
2516
2765
|
}
|
|
2766
|
+
this.maybeShowStarPrompt();
|
|
2517
2767
|
}
|
|
2768
|
+
await this.loadRecentConfigs();
|
|
2518
2769
|
} catch (e) {
|
|
2519
2770
|
this.initError = '连接失败: ' + e.message;
|
|
2520
2771
|
} finally {
|
|
@@ -2522,8 +2773,129 @@
|
|
|
2522
2773
|
}
|
|
2523
2774
|
},
|
|
2524
2775
|
|
|
2776
|
+
async loadModelsForProvider(providerName) {
|
|
2777
|
+
this.codexModelsLoading = true;
|
|
2778
|
+
if (!providerName) {
|
|
2779
|
+
this.models = [];
|
|
2780
|
+
this.modelsSource = 'unlimited';
|
|
2781
|
+
this.modelsHasCurrent = true;
|
|
2782
|
+
this.codexModelsLoading = false;
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2785
|
+
try {
|
|
2786
|
+
const res = await api('models', { provider: providerName });
|
|
2787
|
+
if (res.unlimited) {
|
|
2788
|
+
this.models = [];
|
|
2789
|
+
this.modelsSource = 'unlimited';
|
|
2790
|
+
this.modelsHasCurrent = true;
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
if (res.error) {
|
|
2794
|
+
this.showMessage('模型列表获取失败: ' + res.error, 'error');
|
|
2795
|
+
this.models = [];
|
|
2796
|
+
this.modelsSource = 'error';
|
|
2797
|
+
this.modelsHasCurrent = true;
|
|
2798
|
+
return;
|
|
2799
|
+
}
|
|
2800
|
+
const list = Array.isArray(res.models) ? res.models : [];
|
|
2801
|
+
this.models = list;
|
|
2802
|
+
this.modelsSource = res.source || 'remote';
|
|
2803
|
+
this.modelsHasCurrent = !!this.currentModel && list.includes(this.currentModel);
|
|
2804
|
+
} catch (e) {
|
|
2805
|
+
this.showMessage('模型列表获取失败: ' + e.message, 'error');
|
|
2806
|
+
this.models = [];
|
|
2807
|
+
this.modelsSource = 'error';
|
|
2808
|
+
this.modelsHasCurrent = true;
|
|
2809
|
+
} finally {
|
|
2810
|
+
this.codexModelsLoading = false;
|
|
2811
|
+
}
|
|
2812
|
+
},
|
|
2813
|
+
|
|
2814
|
+
getCurrentClaudeConfig() {
|
|
2815
|
+
if (!this.currentClaudeConfig) return null;
|
|
2816
|
+
return this.claudeConfigs[this.currentClaudeConfig] || null;
|
|
2817
|
+
},
|
|
2818
|
+
|
|
2819
|
+
syncClaudeModelFromConfig() {
|
|
2820
|
+
const config = this.getCurrentClaudeConfig();
|
|
2821
|
+
this.currentClaudeModel = config && config.model ? config.model : '';
|
|
2822
|
+
},
|
|
2823
|
+
|
|
2824
|
+
refreshClaudeModelContext() {
|
|
2825
|
+
this.syncClaudeModelFromConfig();
|
|
2826
|
+
this.loadClaudeModels();
|
|
2827
|
+
},
|
|
2828
|
+
|
|
2829
|
+
resetClaudeModelsState() {
|
|
2830
|
+
this.claudeModels = [];
|
|
2831
|
+
this.claudeModelsSource = 'idle';
|
|
2832
|
+
this.claudeModelsHasCurrent = true;
|
|
2833
|
+
this.claudeModelsLoading = false;
|
|
2834
|
+
},
|
|
2835
|
+
|
|
2836
|
+
updateClaudeModelsCurrent() {
|
|
2837
|
+
const currentModel = (this.currentClaudeModel || '').trim();
|
|
2838
|
+
this.claudeModelsHasCurrent = !!currentModel && this.claudeModels.includes(currentModel);
|
|
2839
|
+
},
|
|
2840
|
+
|
|
2841
|
+
async loadClaudeModels() {
|
|
2842
|
+
const config = this.getCurrentClaudeConfig();
|
|
2843
|
+
const baseUrl = (config.baseUrl || '').trim();
|
|
2844
|
+
const apiKey = (config.apiKey || '').trim();
|
|
2845
|
+
|
|
2846
|
+
if (!baseUrl) {
|
|
2847
|
+
this.resetClaudeModelsState();
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
this.claudeModelsLoading = true;
|
|
2852
|
+
try {
|
|
2853
|
+
const res = await api('models-by-url', { baseUrl, apiKey });
|
|
2854
|
+
if (res.unlimited) {
|
|
2855
|
+
this.claudeModels = [];
|
|
2856
|
+
this.claudeModelsSource = 'unlimited';
|
|
2857
|
+
this.claudeModelsHasCurrent = true;
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
if (res.error) {
|
|
2861
|
+
this.showMessage('模型列表获取失败: ' + res.error, 'error');
|
|
2862
|
+
this.claudeModels = [];
|
|
2863
|
+
this.claudeModelsSource = 'error';
|
|
2864
|
+
this.claudeModelsHasCurrent = true;
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
const list = Array.isArray(res.models) ? res.models : [];
|
|
2868
|
+
this.claudeModels = list;
|
|
2869
|
+
this.claudeModelsSource = res.source || 'remote';
|
|
2870
|
+
this.updateClaudeModelsCurrent();
|
|
2871
|
+
} catch (e) {
|
|
2872
|
+
this.showMessage('模型列表获取失败: ' + e.message, 'error');
|
|
2873
|
+
this.claudeModels = [];
|
|
2874
|
+
this.claudeModelsSource = 'error';
|
|
2875
|
+
this.claudeModelsHasCurrent = true;
|
|
2876
|
+
} finally {
|
|
2877
|
+
this.claudeModelsLoading = false;
|
|
2878
|
+
}
|
|
2879
|
+
},
|
|
2880
|
+
|
|
2881
|
+
openClaudeConfigModal() {
|
|
2882
|
+
this.showClaudeConfigModal = true;
|
|
2883
|
+
},
|
|
2884
|
+
|
|
2885
|
+
maybeShowStarPrompt() {
|
|
2886
|
+
const storageKey = 'codexmateStarPrompted';
|
|
2887
|
+
if (localStorage.getItem(storageKey)) {
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
this.showMessage('如果 Codex Mate 对你有帮助,欢迎到 GitHub 点个 Star。', 'info');
|
|
2891
|
+
localStorage.setItem(storageKey, '1');
|
|
2892
|
+
},
|
|
2893
|
+
|
|
2525
2894
|
switchConfigMode(mode) {
|
|
2526
2895
|
this.configMode = mode;
|
|
2896
|
+
if (mode === 'claude') {
|
|
2897
|
+
this.refreshClaudeModelContext();
|
|
2898
|
+
}
|
|
2527
2899
|
if (mode === 'sessions' && this.sessionsList.length === 0) {
|
|
2528
2900
|
this.loadSessions();
|
|
2529
2901
|
}
|
|
@@ -2915,6 +3287,7 @@
|
|
|
2915
3287
|
|
|
2916
3288
|
async switchProvider(name) {
|
|
2917
3289
|
this.currentProvider = name;
|
|
3290
|
+
await this.loadModelsForProvider(name);
|
|
2918
3291
|
await this.openConfigTemplateEditor();
|
|
2919
3292
|
},
|
|
2920
3293
|
|
|
@@ -2922,6 +3295,60 @@
|
|
|
2922
3295
|
await this.openConfigTemplateEditor();
|
|
2923
3296
|
},
|
|
2924
3297
|
|
|
3298
|
+
async loadRecentConfigs() {
|
|
3299
|
+
this.recentLoading = true;
|
|
3300
|
+
try {
|
|
3301
|
+
const res = await api('get-recent-configs');
|
|
3302
|
+
if (res && Array.isArray(res.items)) {
|
|
3303
|
+
this.recentConfigs = res.items;
|
|
3304
|
+
} else {
|
|
3305
|
+
this.recentConfigs = [];
|
|
3306
|
+
}
|
|
3307
|
+
} catch (e) {
|
|
3308
|
+
this.recentConfigs = [];
|
|
3309
|
+
} finally {
|
|
3310
|
+
this.recentLoading = false;
|
|
3311
|
+
}
|
|
3312
|
+
},
|
|
3313
|
+
|
|
3314
|
+
async applyRecentConfig(item) {
|
|
3315
|
+
if (!item || !item.provider || !item.model) {
|
|
3316
|
+
this.showMessage('最近配置无效,无法应用', 'error');
|
|
3317
|
+
return;
|
|
3318
|
+
}
|
|
3319
|
+
this.currentProvider = item.provider;
|
|
3320
|
+
this.currentModel = item.model;
|
|
3321
|
+
await this.openConfigTemplateEditor({
|
|
3322
|
+
appendHint: '最近使用配置,确认后将写入 config.toml'
|
|
3323
|
+
});
|
|
3324
|
+
},
|
|
3325
|
+
|
|
3326
|
+
async runHealthCheck() {
|
|
3327
|
+
this.healthCheckLoading = true;
|
|
3328
|
+
this.healthCheckResult = null;
|
|
3329
|
+
try {
|
|
3330
|
+
const res = await api('config-health-check', {
|
|
3331
|
+
remote: this.healthCheckRemote
|
|
3332
|
+
});
|
|
3333
|
+
if (res && typeof res === 'object') {
|
|
3334
|
+
this.healthCheckResult = res;
|
|
3335
|
+
if (res.ok) {
|
|
3336
|
+
this.showMessage('健康检查通过', 'success');
|
|
3337
|
+
} else {
|
|
3338
|
+
this.showMessage('发现配置问题,请查看详情', 'error');
|
|
3339
|
+
}
|
|
3340
|
+
} else {
|
|
3341
|
+
this.healthCheckResult = null;
|
|
3342
|
+
this.showMessage('健康检查失败:返回数据异常', 'error');
|
|
3343
|
+
}
|
|
3344
|
+
} catch (e) {
|
|
3345
|
+
this.healthCheckResult = null;
|
|
3346
|
+
this.showMessage('健康检查失败: ' + e.message, 'error');
|
|
3347
|
+
} finally {
|
|
3348
|
+
this.healthCheckLoading = false;
|
|
3349
|
+
}
|
|
3350
|
+
},
|
|
3351
|
+
|
|
2925
3352
|
escapeTomlString(value) {
|
|
2926
3353
|
return String(value || '')
|
|
2927
3354
|
.replace(/\\/g, '\\\\')
|
|
@@ -2984,84 +3411,84 @@
|
|
|
2984
3411
|
}
|
|
2985
3412
|
},
|
|
2986
3413
|
|
|
2987
|
-
async openAgentsEditor() {
|
|
2988
|
-
this.setAgentsModalContext('codex');
|
|
2989
|
-
this.agentsLoading = true;
|
|
2990
|
-
try {
|
|
2991
|
-
const res = await api('get-agents-file');
|
|
2992
|
-
if (res.error) {
|
|
2993
|
-
this.showMessage(res.error, 'error');
|
|
3414
|
+
async openAgentsEditor() {
|
|
3415
|
+
this.setAgentsModalContext('codex');
|
|
3416
|
+
this.agentsLoading = true;
|
|
3417
|
+
try {
|
|
3418
|
+
const res = await api('get-agents-file');
|
|
3419
|
+
if (res.error) {
|
|
3420
|
+
this.showMessage(res.error, 'error');
|
|
3421
|
+
return;
|
|
3422
|
+
}
|
|
3423
|
+
this.agentsContent = res.content || '';
|
|
3424
|
+
this.agentsPath = res.path || '';
|
|
3425
|
+
this.agentsExists = !!res.exists;
|
|
3426
|
+
this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
3427
|
+
this.showAgentsModal = true;
|
|
3428
|
+
} catch (e) {
|
|
3429
|
+
this.showMessage('加载 AGENTS.md 失败: ' + e.message, 'error');
|
|
3430
|
+
} finally {
|
|
3431
|
+
this.agentsLoading = false;
|
|
3432
|
+
}
|
|
3433
|
+
},
|
|
3434
|
+
|
|
3435
|
+
async openOpenclawAgentsEditor() {
|
|
3436
|
+
this.setAgentsModalContext('openclaw');
|
|
3437
|
+
this.agentsLoading = true;
|
|
3438
|
+
try {
|
|
3439
|
+
const res = await api('get-openclaw-agents-file');
|
|
3440
|
+
if (res.error) {
|
|
3441
|
+
this.showMessage(res.error, 'error');
|
|
2994
3442
|
return;
|
|
2995
3443
|
}
|
|
3444
|
+
if (res.configError) {
|
|
3445
|
+
this.showMessage(`OpenClaw 配置解析失败,已使用默认 Workspace:${res.configError}`, 'error');
|
|
3446
|
+
}
|
|
2996
3447
|
this.agentsContent = res.content || '';
|
|
2997
3448
|
this.agentsPath = res.path || '';
|
|
2998
3449
|
this.agentsExists = !!res.exists;
|
|
2999
|
-
this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
3000
|
-
this.showAgentsModal = true;
|
|
3001
|
-
} catch (e) {
|
|
3002
|
-
this.showMessage('加载 AGENTS.md 失败: ' + e.message, 'error');
|
|
3003
|
-
} finally {
|
|
3004
|
-
this.agentsLoading = false;
|
|
3005
|
-
}
|
|
3006
|
-
},
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
this.
|
|
3010
|
-
this.
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
},
|
|
3042
|
-
|
|
3043
|
-
closeAgentsModal() {
|
|
3044
|
-
this.showAgentsModal = false;
|
|
3045
|
-
this.agentsContent = '';
|
|
3046
|
-
this.agentsPath = '';
|
|
3047
|
-
this.agentsExists = false;
|
|
3048
|
-
this.agentsLineEnding = '\n';
|
|
3049
|
-
this.agentsSaving = false;
|
|
3050
|
-
this.setAgentsModalContext('codex');
|
|
3051
|
-
},
|
|
3052
|
-
|
|
3053
|
-
async applyAgentsContent() {
|
|
3054
|
-
this.agentsSaving = true;
|
|
3055
|
-
try {
|
|
3056
|
-
const action = this.agentsContext === 'openclaw'
|
|
3057
|
-
? 'apply-openclaw-agents-file'
|
|
3058
|
-
: 'apply-agents-file';
|
|
3059
|
-
const res = await api(action, {
|
|
3060
|
-
content: this.agentsContent,
|
|
3061
|
-
lineEnding: this.agentsLineEnding
|
|
3062
|
-
});
|
|
3063
|
-
if (res.error) {
|
|
3064
|
-
this.showMessage(res.error, 'error');
|
|
3450
|
+
this.agentsLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
3451
|
+
this.showAgentsModal = true;
|
|
3452
|
+
} catch (e) {
|
|
3453
|
+
this.showMessage('加载 OpenClaw AGENTS.md 失败: ' + e.message, 'error');
|
|
3454
|
+
} finally {
|
|
3455
|
+
this.agentsLoading = false;
|
|
3456
|
+
}
|
|
3457
|
+
},
|
|
3458
|
+
|
|
3459
|
+
setAgentsModalContext(context) {
|
|
3460
|
+
this.agentsContext = context === 'openclaw' ? 'openclaw' : 'codex';
|
|
3461
|
+
if (this.agentsContext === 'openclaw') {
|
|
3462
|
+
this.agentsModalTitle = 'OpenClaw AGENTS.md 编辑器';
|
|
3463
|
+
this.agentsModalHint = '保存后会写入 OpenClaw Workspace 下的 AGENTS.md。';
|
|
3464
|
+
} else {
|
|
3465
|
+
this.agentsModalTitle = 'AGENTS.md 编辑器';
|
|
3466
|
+
this.agentsModalHint = '保存后会写入目标 AGENTS.md(与 config.toml 同级)。';
|
|
3467
|
+
}
|
|
3468
|
+
},
|
|
3469
|
+
|
|
3470
|
+
closeAgentsModal() {
|
|
3471
|
+
this.showAgentsModal = false;
|
|
3472
|
+
this.agentsContent = '';
|
|
3473
|
+
this.agentsPath = '';
|
|
3474
|
+
this.agentsExists = false;
|
|
3475
|
+
this.agentsLineEnding = '\n';
|
|
3476
|
+
this.agentsSaving = false;
|
|
3477
|
+
this.setAgentsModalContext('codex');
|
|
3478
|
+
},
|
|
3479
|
+
|
|
3480
|
+
async applyAgentsContent() {
|
|
3481
|
+
this.agentsSaving = true;
|
|
3482
|
+
try {
|
|
3483
|
+
const action = this.agentsContext === 'openclaw'
|
|
3484
|
+
? 'apply-openclaw-agents-file'
|
|
3485
|
+
: 'apply-agents-file';
|
|
3486
|
+
const res = await api(action, {
|
|
3487
|
+
content: this.agentsContent,
|
|
3488
|
+
lineEnding: this.agentsLineEnding
|
|
3489
|
+
});
|
|
3490
|
+
if (res.error) {
|
|
3491
|
+
this.showMessage(res.error, 'error');
|
|
3065
3492
|
return;
|
|
3066
3493
|
}
|
|
3067
3494
|
this.showMessage('AGENTS.md 已保存', 'success');
|
|
@@ -3186,6 +3613,35 @@
|
|
|
3186
3613
|
|
|
3187
3614
|
switchClaudeConfig(name) {
|
|
3188
3615
|
this.currentClaudeConfig = name;
|
|
3616
|
+
this.refreshClaudeModelContext();
|
|
3617
|
+
},
|
|
3618
|
+
|
|
3619
|
+
onClaudeModelChange() {
|
|
3620
|
+
const name = this.currentClaudeConfig;
|
|
3621
|
+
if (!name) {
|
|
3622
|
+
this.showMessage('请先选择配置', 'error');
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
const model = (this.currentClaudeModel || '').trim();
|
|
3626
|
+
if (!model) {
|
|
3627
|
+
this.showMessage('请输入模型', 'error');
|
|
3628
|
+
return;
|
|
3629
|
+
}
|
|
3630
|
+
const existing = this.claudeConfigs[name] || {};
|
|
3631
|
+
this.currentClaudeModel = model;
|
|
3632
|
+
this.claudeConfigs[name] = {
|
|
3633
|
+
apiKey: existing.apiKey || '',
|
|
3634
|
+
baseUrl: existing.baseUrl || '',
|
|
3635
|
+
model: model,
|
|
3636
|
+
hasKey: !!existing.apiKey
|
|
3637
|
+
};
|
|
3638
|
+
this.saveClaudeConfigs();
|
|
3639
|
+
this.updateClaudeModelsCurrent();
|
|
3640
|
+
if (!this.claudeConfigs[name].apiKey) {
|
|
3641
|
+
this.showMessage('该配置未设置 API Key,请先编辑', 'error');
|
|
3642
|
+
return;
|
|
3643
|
+
}
|
|
3644
|
+
this.applyClaudeConfig(name);
|
|
3189
3645
|
},
|
|
3190
3646
|
|
|
3191
3647
|
saveClaudeConfigs() {
|
|
@@ -3214,6 +3670,9 @@
|
|
|
3214
3670
|
this.saveClaudeConfigs();
|
|
3215
3671
|
this.showMessage('配置已更新', 'success');
|
|
3216
3672
|
this.closeEditConfigModal();
|
|
3673
|
+
if (name === this.currentClaudeConfig) {
|
|
3674
|
+
this.refreshClaudeModelContext();
|
|
3675
|
+
}
|
|
3217
3676
|
},
|
|
3218
3677
|
|
|
3219
3678
|
closeEditConfigModal() {
|
|
@@ -3233,7 +3692,12 @@
|
|
|
3233
3692
|
|
|
3234
3693
|
const config = this.claudeConfigs[name];
|
|
3235
3694
|
if (!config.apiKey) {
|
|
3236
|
-
|
|
3695
|
+
this.showMessage('已保存,未应用:请先输入 API Key', 'info');
|
|
3696
|
+
this.closeEditConfigModal();
|
|
3697
|
+
if (name === this.currentClaudeConfig) {
|
|
3698
|
+
this.refreshClaudeModelContext();
|
|
3699
|
+
}
|
|
3700
|
+
return;
|
|
3237
3701
|
}
|
|
3238
3702
|
|
|
3239
3703
|
const res = await api('apply-claude-config', { config });
|
|
@@ -3243,30 +3707,9 @@
|
|
|
3243
3707
|
const targetTip = res.targetPath ? `(${res.targetPath})` : '';
|
|
3244
3708
|
this.showMessage(`已保存并应用到 Claude 配置${targetTip}`, 'success');
|
|
3245
3709
|
this.closeEditConfigModal();
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
async saveAndApplyEnvCompat() {
|
|
3250
|
-
const name = this.editingConfig.name;
|
|
3251
|
-
this.claudeConfigs[name] = {
|
|
3252
|
-
apiKey: this.editingConfig.apiKey,
|
|
3253
|
-
baseUrl: this.editingConfig.baseUrl,
|
|
3254
|
-
model: this.editingConfig.model,
|
|
3255
|
-
hasKey: !!this.editingConfig.apiKey
|
|
3256
|
-
};
|
|
3257
|
-
this.saveClaudeConfigs();
|
|
3258
|
-
|
|
3259
|
-
const config = this.claudeConfigs[name];
|
|
3260
|
-
if (!config.apiKey) {
|
|
3261
|
-
return this.showMessage('请先输入 API Key', 'error');
|
|
3262
|
-
}
|
|
3263
|
-
|
|
3264
|
-
const res = await api('apply-env', { config });
|
|
3265
|
-
if (res.error || res.success === false) {
|
|
3266
|
-
this.showMessage(res.error || '应用环境变量失败', 'error');
|
|
3267
|
-
} else {
|
|
3268
|
-
this.showMessage('已保存并应用到系统环境变量(兼容模式)', 'success');
|
|
3269
|
-
this.closeEditConfigModal();
|
|
3710
|
+
if (name === this.currentClaudeConfig) {
|
|
3711
|
+
this.refreshClaudeModelContext();
|
|
3712
|
+
}
|
|
3270
3713
|
}
|
|
3271
3714
|
},
|
|
3272
3715
|
|
|
@@ -3290,6 +3733,7 @@
|
|
|
3290
3733
|
this.saveClaudeConfigs();
|
|
3291
3734
|
this.showMessage('配置已添加', 'success');
|
|
3292
3735
|
this.closeClaudeConfigModal();
|
|
3736
|
+
this.refreshClaudeModelContext();
|
|
3293
3737
|
},
|
|
3294
3738
|
|
|
3295
3739
|
deleteClaudeConfig(name) {
|
|
@@ -3305,10 +3749,12 @@
|
|
|
3305
3749
|
}
|
|
3306
3750
|
this.saveClaudeConfigs();
|
|
3307
3751
|
this.showMessage('配置已删除', 'success');
|
|
3752
|
+
this.refreshClaudeModelContext();
|
|
3308
3753
|
},
|
|
3309
3754
|
|
|
3310
3755
|
async applyClaudeConfig(name) {
|
|
3311
3756
|
this.currentClaudeConfig = name;
|
|
3757
|
+
this.refreshClaudeModelContext();
|
|
3312
3758
|
const config = this.claudeConfigs[name];
|
|
3313
3759
|
|
|
3314
3760
|
if (!config.apiKey) {
|
|
@@ -3324,187 +3770,187 @@
|
|
|
3324
3770
|
}
|
|
3325
3771
|
},
|
|
3326
3772
|
|
|
3327
|
-
closeClaudeConfigModal() {
|
|
3328
|
-
this.showClaudeConfigModal = false;
|
|
3329
|
-
this.newClaudeConfig = {
|
|
3330
|
-
name: '',
|
|
3331
|
-
apiKey: '',
|
|
3332
|
-
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
3333
|
-
model: 'glm-4.7'
|
|
3334
|
-
};
|
|
3335
|
-
},
|
|
3336
|
-
|
|
3337
|
-
openclawHasContent(config) {
|
|
3338
|
-
return !!(config && typeof config.content === 'string' && config.content.trim());
|
|
3339
|
-
},
|
|
3340
|
-
|
|
3341
|
-
openclawSubtitle(config) {
|
|
3342
|
-
if (!this.openclawHasContent(config)) {
|
|
3343
|
-
return '未设置配置';
|
|
3344
|
-
}
|
|
3345
|
-
const length = config.content.trim().length;
|
|
3346
|
-
return `已保存 ${length} 字符`;
|
|
3347
|
-
},
|
|
3348
|
-
|
|
3349
|
-
saveOpenclawConfigs() {
|
|
3350
|
-
localStorage.setItem('openclawConfigs', JSON.stringify(this.openclawConfigs));
|
|
3351
|
-
},
|
|
3352
|
-
|
|
3353
|
-
openOpenclawAddModal() {
|
|
3354
|
-
this.openclawEditorTitle = '添加 OpenClaw 配置';
|
|
3355
|
-
this.openclawEditing = {
|
|
3356
|
-
name: '',
|
|
3357
|
-
content: DEFAULT_OPENCLAW_TEMPLATE,
|
|
3358
|
-
lockName: false
|
|
3359
|
-
};
|
|
3360
|
-
this.openclawConfigPath = '';
|
|
3361
|
-
this.openclawConfigExists = false;
|
|
3362
|
-
this.openclawLineEnding = '\n';
|
|
3363
|
-
this.showOpenclawConfigModal = true;
|
|
3364
|
-
},
|
|
3365
|
-
|
|
3366
|
-
openOpenclawEditModal(name) {
|
|
3367
|
-
const config = this.openclawConfigs[name];
|
|
3368
|
-
this.openclawEditorTitle = `编辑 OpenClaw 配置: ${name}`;
|
|
3369
|
-
this.openclawEditing = {
|
|
3370
|
-
name,
|
|
3371
|
-
content: (config && config.content) ? config.content : '',
|
|
3372
|
-
lockName: true
|
|
3373
|
-
};
|
|
3374
|
-
this.showOpenclawConfigModal = true;
|
|
3375
|
-
},
|
|
3376
|
-
|
|
3377
|
-
closeOpenclawConfigModal() {
|
|
3378
|
-
this.showOpenclawConfigModal = false;
|
|
3379
|
-
this.openclawEditing = { name: '', content: '', lockName: false };
|
|
3380
|
-
this.openclawSaving = false;
|
|
3381
|
-
this.openclawApplying = false;
|
|
3382
|
-
},
|
|
3383
|
-
|
|
3384
|
-
async loadOpenclawConfigFromFile() {
|
|
3385
|
-
this.openclawFileLoading = true;
|
|
3386
|
-
try {
|
|
3387
|
-
const res = await api('get-openclaw-config');
|
|
3388
|
-
if (res.error) {
|
|
3389
|
-
this.showMessage(res.error, 'error');
|
|
3390
|
-
return;
|
|
3391
|
-
}
|
|
3392
|
-
this.openclawConfigPath = res.path || '';
|
|
3393
|
-
this.openclawConfigExists = !!res.exists;
|
|
3394
|
-
this.openclawLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
3395
|
-
if (res.content && res.content.trim()) {
|
|
3396
|
-
this.openclawEditing.content = res.content;
|
|
3397
|
-
} else if (!this.openclawEditing.content) {
|
|
3398
|
-
this.openclawEditing.content = DEFAULT_OPENCLAW_TEMPLATE;
|
|
3399
|
-
}
|
|
3400
|
-
this.showMessage('已加载当前 OpenClaw 配置', 'success');
|
|
3401
|
-
} catch (e) {
|
|
3402
|
-
this.showMessage('加载 OpenClaw 配置失败: ' + e.message, 'error');
|
|
3403
|
-
} finally {
|
|
3404
|
-
this.openclawFileLoading = false;
|
|
3405
|
-
}
|
|
3406
|
-
},
|
|
3407
|
-
|
|
3408
|
-
persistOpenclawConfig({ closeModal = true } = {}) {
|
|
3409
|
-
if (!this.openclawEditing.name || !this.openclawEditing.name.trim()) {
|
|
3410
|
-
this.showMessage('请输入配置名称', 'error');
|
|
3411
|
-
return '';
|
|
3412
|
-
}
|
|
3413
|
-
const name = this.openclawEditing.name.trim();
|
|
3414
|
-
if (!this.openclawEditing.lockName && this.openclawConfigs[name]) {
|
|
3415
|
-
this.showMessage('配置名称已存在', 'error');
|
|
3416
|
-
return '';
|
|
3417
|
-
}
|
|
3418
|
-
if (!this.openclawEditing.content || !this.openclawEditing.content.trim()) {
|
|
3419
|
-
this.showMessage('配置内容不能为空', 'error');
|
|
3420
|
-
return '';
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
|
-
this.openclawConfigs[name] = {
|
|
3424
|
-
content: this.openclawEditing.content
|
|
3425
|
-
};
|
|
3426
|
-
this.currentOpenclawConfig = name;
|
|
3427
|
-
this.saveOpenclawConfigs();
|
|
3428
|
-
if (closeModal) {
|
|
3429
|
-
this.closeOpenclawConfigModal();
|
|
3430
|
-
}
|
|
3431
|
-
return name;
|
|
3432
|
-
},
|
|
3433
|
-
|
|
3434
|
-
async saveOpenclawConfig() {
|
|
3435
|
-
this.openclawSaving = true;
|
|
3436
|
-
try {
|
|
3437
|
-
const name = this.persistOpenclawConfig();
|
|
3438
|
-
if (!name) return;
|
|
3439
|
-
this.showMessage('OpenClaw 配置已保存', 'success');
|
|
3440
|
-
} finally {
|
|
3441
|
-
this.openclawSaving = false;
|
|
3442
|
-
}
|
|
3443
|
-
},
|
|
3444
|
-
|
|
3445
|
-
async saveAndApplyOpenclawConfig() {
|
|
3446
|
-
this.openclawApplying = true;
|
|
3447
|
-
try {
|
|
3448
|
-
const name = this.persistOpenclawConfig({ closeModal: false });
|
|
3449
|
-
if (!name) return;
|
|
3450
|
-
const config = this.openclawConfigs[name];
|
|
3451
|
-
const res = await api('apply-openclaw-config', {
|
|
3452
|
-
content: config.content,
|
|
3453
|
-
lineEnding: this.openclawLineEnding
|
|
3454
|
-
});
|
|
3455
|
-
if (res.error || res.success === false) {
|
|
3456
|
-
this.showMessage(res.error || '应用 OpenClaw 配置失败', 'error');
|
|
3457
|
-
return;
|
|
3458
|
-
}
|
|
3459
|
-
this.openclawConfigPath = res.targetPath || this.openclawConfigPath;
|
|
3460
|
-
this.openclawConfigExists = true;
|
|
3461
|
-
const targetTip = res.targetPath ? `(${res.targetPath})` : '';
|
|
3462
|
-
this.showMessage(`已保存并应用 OpenClaw 配置${targetTip}`, 'success');
|
|
3463
|
-
this.closeOpenclawConfigModal();
|
|
3464
|
-
} catch (e) {
|
|
3465
|
-
this.showMessage('应用 OpenClaw 配置失败: ' + e.message, 'error');
|
|
3466
|
-
} finally {
|
|
3467
|
-
this.openclawApplying = false;
|
|
3468
|
-
}
|
|
3469
|
-
},
|
|
3470
|
-
|
|
3471
|
-
deleteOpenclawConfig(name) {
|
|
3472
|
-
if (Object.keys(this.openclawConfigs).length <= 1) {
|
|
3473
|
-
return this.showMessage('至少保留一个配置', 'error');
|
|
3474
|
-
}
|
|
3475
|
-
if (!confirm(`确定删除配置 "${name}"?`)) return;
|
|
3476
|
-
delete this.openclawConfigs[name];
|
|
3477
|
-
if (this.currentOpenclawConfig === name) {
|
|
3478
|
-
this.currentOpenclawConfig = Object.keys(this.openclawConfigs)[0];
|
|
3479
|
-
}
|
|
3480
|
-
this.saveOpenclawConfigs();
|
|
3481
|
-
this.showMessage('OpenClaw 配置已删除', 'success');
|
|
3482
|
-
},
|
|
3483
|
-
|
|
3484
|
-
async applyOpenclawConfig(name) {
|
|
3485
|
-
this.currentOpenclawConfig = name;
|
|
3486
|
-
const config = this.openclawConfigs[name];
|
|
3487
|
-
if (!this.openclawHasContent(config)) {
|
|
3488
|
-
return this.showMessage('该配置为空,请先编辑', 'error');
|
|
3489
|
-
}
|
|
3490
|
-
const res = await api('apply-openclaw-config', {
|
|
3491
|
-
content: config.content,
|
|
3492
|
-
lineEnding: this.openclawLineEnding
|
|
3493
|
-
});
|
|
3494
|
-
if (res.error || res.success === false) {
|
|
3495
|
-
this.showMessage(res.error || '应用 OpenClaw 配置失败', 'error');
|
|
3496
|
-
} else {
|
|
3497
|
-
this.openclawConfigPath = res.targetPath || this.openclawConfigPath;
|
|
3498
|
-
this.openclawConfigExists = true;
|
|
3499
|
-
const targetTip = res.targetPath ? `(${res.targetPath})` : '';
|
|
3500
|
-
this.showMessage(`已应用 OpenClaw 配置: ${name}${targetTip}`, 'success');
|
|
3501
|
-
}
|
|
3502
|
-
},
|
|
3503
|
-
|
|
3504
|
-
formatLatency(result) {
|
|
3505
|
-
if (!result) return '';
|
|
3506
|
-
if (!result.ok) return result.status ? `ERR ${result.status}` : 'ERR';
|
|
3507
|
-
const ms = typeof result.durationMs === 'number' ? result.durationMs : 0;
|
|
3773
|
+
closeClaudeConfigModal() {
|
|
3774
|
+
this.showClaudeConfigModal = false;
|
|
3775
|
+
this.newClaudeConfig = {
|
|
3776
|
+
name: '',
|
|
3777
|
+
apiKey: '',
|
|
3778
|
+
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
3779
|
+
model: 'glm-4.7'
|
|
3780
|
+
};
|
|
3781
|
+
},
|
|
3782
|
+
|
|
3783
|
+
openclawHasContent(config) {
|
|
3784
|
+
return !!(config && typeof config.content === 'string' && config.content.trim());
|
|
3785
|
+
},
|
|
3786
|
+
|
|
3787
|
+
openclawSubtitle(config) {
|
|
3788
|
+
if (!this.openclawHasContent(config)) {
|
|
3789
|
+
return '未设置配置';
|
|
3790
|
+
}
|
|
3791
|
+
const length = config.content.trim().length;
|
|
3792
|
+
return `已保存 ${length} 字符`;
|
|
3793
|
+
},
|
|
3794
|
+
|
|
3795
|
+
saveOpenclawConfigs() {
|
|
3796
|
+
localStorage.setItem('openclawConfigs', JSON.stringify(this.openclawConfigs));
|
|
3797
|
+
},
|
|
3798
|
+
|
|
3799
|
+
openOpenclawAddModal() {
|
|
3800
|
+
this.openclawEditorTitle = '添加 OpenClaw 配置';
|
|
3801
|
+
this.openclawEditing = {
|
|
3802
|
+
name: '',
|
|
3803
|
+
content: DEFAULT_OPENCLAW_TEMPLATE,
|
|
3804
|
+
lockName: false
|
|
3805
|
+
};
|
|
3806
|
+
this.openclawConfigPath = '';
|
|
3807
|
+
this.openclawConfigExists = false;
|
|
3808
|
+
this.openclawLineEnding = '\n';
|
|
3809
|
+
this.showOpenclawConfigModal = true;
|
|
3810
|
+
},
|
|
3811
|
+
|
|
3812
|
+
openOpenclawEditModal(name) {
|
|
3813
|
+
const config = this.openclawConfigs[name];
|
|
3814
|
+
this.openclawEditorTitle = `编辑 OpenClaw 配置: ${name}`;
|
|
3815
|
+
this.openclawEditing = {
|
|
3816
|
+
name,
|
|
3817
|
+
content: (config && config.content) ? config.content : '',
|
|
3818
|
+
lockName: true
|
|
3819
|
+
};
|
|
3820
|
+
this.showOpenclawConfigModal = true;
|
|
3821
|
+
},
|
|
3822
|
+
|
|
3823
|
+
closeOpenclawConfigModal() {
|
|
3824
|
+
this.showOpenclawConfigModal = false;
|
|
3825
|
+
this.openclawEditing = { name: '', content: '', lockName: false };
|
|
3826
|
+
this.openclawSaving = false;
|
|
3827
|
+
this.openclawApplying = false;
|
|
3828
|
+
},
|
|
3829
|
+
|
|
3830
|
+
async loadOpenclawConfigFromFile() {
|
|
3831
|
+
this.openclawFileLoading = true;
|
|
3832
|
+
try {
|
|
3833
|
+
const res = await api('get-openclaw-config');
|
|
3834
|
+
if (res.error) {
|
|
3835
|
+
this.showMessage(res.error, 'error');
|
|
3836
|
+
return;
|
|
3837
|
+
}
|
|
3838
|
+
this.openclawConfigPath = res.path || '';
|
|
3839
|
+
this.openclawConfigExists = !!res.exists;
|
|
3840
|
+
this.openclawLineEnding = res.lineEnding === '\r\n' ? '\r\n' : '\n';
|
|
3841
|
+
if (res.content && res.content.trim()) {
|
|
3842
|
+
this.openclawEditing.content = res.content;
|
|
3843
|
+
} else if (!this.openclawEditing.content) {
|
|
3844
|
+
this.openclawEditing.content = DEFAULT_OPENCLAW_TEMPLATE;
|
|
3845
|
+
}
|
|
3846
|
+
this.showMessage('已加载当前 OpenClaw 配置', 'success');
|
|
3847
|
+
} catch (e) {
|
|
3848
|
+
this.showMessage('加载 OpenClaw 配置失败: ' + e.message, 'error');
|
|
3849
|
+
} finally {
|
|
3850
|
+
this.openclawFileLoading = false;
|
|
3851
|
+
}
|
|
3852
|
+
},
|
|
3853
|
+
|
|
3854
|
+
persistOpenclawConfig({ closeModal = true } = {}) {
|
|
3855
|
+
if (!this.openclawEditing.name || !this.openclawEditing.name.trim()) {
|
|
3856
|
+
this.showMessage('请输入配置名称', 'error');
|
|
3857
|
+
return '';
|
|
3858
|
+
}
|
|
3859
|
+
const name = this.openclawEditing.name.trim();
|
|
3860
|
+
if (!this.openclawEditing.lockName && this.openclawConfigs[name]) {
|
|
3861
|
+
this.showMessage('配置名称已存在', 'error');
|
|
3862
|
+
return '';
|
|
3863
|
+
}
|
|
3864
|
+
if (!this.openclawEditing.content || !this.openclawEditing.content.trim()) {
|
|
3865
|
+
this.showMessage('配置内容不能为空', 'error');
|
|
3866
|
+
return '';
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3869
|
+
this.openclawConfigs[name] = {
|
|
3870
|
+
content: this.openclawEditing.content
|
|
3871
|
+
};
|
|
3872
|
+
this.currentOpenclawConfig = name;
|
|
3873
|
+
this.saveOpenclawConfigs();
|
|
3874
|
+
if (closeModal) {
|
|
3875
|
+
this.closeOpenclawConfigModal();
|
|
3876
|
+
}
|
|
3877
|
+
return name;
|
|
3878
|
+
},
|
|
3879
|
+
|
|
3880
|
+
async saveOpenclawConfig() {
|
|
3881
|
+
this.openclawSaving = true;
|
|
3882
|
+
try {
|
|
3883
|
+
const name = this.persistOpenclawConfig();
|
|
3884
|
+
if (!name) return;
|
|
3885
|
+
this.showMessage('OpenClaw 配置已保存', 'success');
|
|
3886
|
+
} finally {
|
|
3887
|
+
this.openclawSaving = false;
|
|
3888
|
+
}
|
|
3889
|
+
},
|
|
3890
|
+
|
|
3891
|
+
async saveAndApplyOpenclawConfig() {
|
|
3892
|
+
this.openclawApplying = true;
|
|
3893
|
+
try {
|
|
3894
|
+
const name = this.persistOpenclawConfig({ closeModal: false });
|
|
3895
|
+
if (!name) return;
|
|
3896
|
+
const config = this.openclawConfigs[name];
|
|
3897
|
+
const res = await api('apply-openclaw-config', {
|
|
3898
|
+
content: config.content,
|
|
3899
|
+
lineEnding: this.openclawLineEnding
|
|
3900
|
+
});
|
|
3901
|
+
if (res.error || res.success === false) {
|
|
3902
|
+
this.showMessage(res.error || '应用 OpenClaw 配置失败', 'error');
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3905
|
+
this.openclawConfigPath = res.targetPath || this.openclawConfigPath;
|
|
3906
|
+
this.openclawConfigExists = true;
|
|
3907
|
+
const targetTip = res.targetPath ? `(${res.targetPath})` : '';
|
|
3908
|
+
this.showMessage(`已保存并应用 OpenClaw 配置${targetTip}`, 'success');
|
|
3909
|
+
this.closeOpenclawConfigModal();
|
|
3910
|
+
} catch (e) {
|
|
3911
|
+
this.showMessage('应用 OpenClaw 配置失败: ' + e.message, 'error');
|
|
3912
|
+
} finally {
|
|
3913
|
+
this.openclawApplying = false;
|
|
3914
|
+
}
|
|
3915
|
+
},
|
|
3916
|
+
|
|
3917
|
+
deleteOpenclawConfig(name) {
|
|
3918
|
+
if (Object.keys(this.openclawConfigs).length <= 1) {
|
|
3919
|
+
return this.showMessage('至少保留一个配置', 'error');
|
|
3920
|
+
}
|
|
3921
|
+
if (!confirm(`确定删除配置 "${name}"?`)) return;
|
|
3922
|
+
delete this.openclawConfigs[name];
|
|
3923
|
+
if (this.currentOpenclawConfig === name) {
|
|
3924
|
+
this.currentOpenclawConfig = Object.keys(this.openclawConfigs)[0];
|
|
3925
|
+
}
|
|
3926
|
+
this.saveOpenclawConfigs();
|
|
3927
|
+
this.showMessage('OpenClaw 配置已删除', 'success');
|
|
3928
|
+
},
|
|
3929
|
+
|
|
3930
|
+
async applyOpenclawConfig(name) {
|
|
3931
|
+
this.currentOpenclawConfig = name;
|
|
3932
|
+
const config = this.openclawConfigs[name];
|
|
3933
|
+
if (!this.openclawHasContent(config)) {
|
|
3934
|
+
return this.showMessage('该配置为空,请先编辑', 'error');
|
|
3935
|
+
}
|
|
3936
|
+
const res = await api('apply-openclaw-config', {
|
|
3937
|
+
content: config.content,
|
|
3938
|
+
lineEnding: this.openclawLineEnding
|
|
3939
|
+
});
|
|
3940
|
+
if (res.error || res.success === false) {
|
|
3941
|
+
this.showMessage(res.error || '应用 OpenClaw 配置失败', 'error');
|
|
3942
|
+
} else {
|
|
3943
|
+
this.openclawConfigPath = res.targetPath || this.openclawConfigPath;
|
|
3944
|
+
this.openclawConfigExists = true;
|
|
3945
|
+
const targetTip = res.targetPath ? `(${res.targetPath})` : '';
|
|
3946
|
+
this.showMessage(`已应用 OpenClaw 配置: ${name}${targetTip}`, 'success');
|
|
3947
|
+
}
|
|
3948
|
+
},
|
|
3949
|
+
|
|
3950
|
+
formatLatency(result) {
|
|
3951
|
+
if (!result) return '';
|
|
3952
|
+
if (!result.ok) return result.status ? `ERR ${result.status}` : 'ERR';
|
|
3953
|
+
const ms = typeof result.durationMs === 'number' ? result.durationMs : 0;
|
|
3508
3954
|
return `${ms}ms`;
|
|
3509
3955
|
},
|
|
3510
3956
|
|
|
@@ -3537,10 +3983,3 @@
|
|
|
3537
3983
|
</script>
|
|
3538
3984
|
</body>
|
|
3539
3985
|
</html>
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|