@memtensor/memos-local-openclaw-plugin 1.0.0 → 1.0.2-beta.1
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.md +8 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +20 -9
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/viewer/html.d.ts +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +223 -70
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +6 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +236 -24
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +41 -0
- package/package.json +1 -1
- package/scripts/postinstall.cjs +72 -0
- package/src/storage/sqlite.ts +19 -9
- package/src/viewer/html.ts +223 -70
- package/src/viewer/server.ts +228 -20
package/src/viewer/html.ts
CHANGED
|
@@ -512,6 +512,12 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
512
512
|
.toggle-slider::before{content:'';position:absolute;height:14px;width:14px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.2s}
|
|
513
513
|
.toggle-switch input:checked+.toggle-slider{background:var(--pri)}
|
|
514
514
|
.toggle-switch input:checked+.toggle-slider::before{transform:translateX(16px)}
|
|
515
|
+
.test-conn-row{display:flex;align-items:center;gap:10px;margin-top:12px;padding-top:10px;border-top:1px dashed var(--border)}
|
|
516
|
+
.test-conn-row .btn{font-size:11px;padding:5px 14px;border:1px solid var(--border);border-radius:6px}
|
|
517
|
+
.test-result{font-size:12px;line-height:1.5;word-break:break-word}
|
|
518
|
+
.test-result.ok{color:#22c55e}
|
|
519
|
+
.test-result.fail{color:var(--rose)}
|
|
520
|
+
.test-result.loading{color:var(--text-muted)}
|
|
515
521
|
.settings-actions{display:flex;gap:12px;justify-content:flex-end;align-items:center;margin-top:16px;padding-top:16px;border-top:1px solid var(--border)}
|
|
516
522
|
.settings-actions .btn{min-width:110px;padding:10px 20px;font-size:13px}
|
|
517
523
|
.settings-actions .btn-primary{background:rgba(99,102,241,.08);color:var(--pri);border:1px solid rgba(99,102,241,.25);font-weight:600}
|
|
@@ -939,9 +945,12 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
939
945
|
<div class="settings-grid">
|
|
940
946
|
<div class="settings-field">
|
|
941
947
|
<label data-i18n="settings.provider">Provider</label>
|
|
942
|
-
<select id="cfgEmbProvider">
|
|
948
|
+
<select id="cfgEmbProvider" onchange="onProviderChange('embedding')">
|
|
943
949
|
<option value="openai_compatible">OpenAI Compatible</option>
|
|
944
950
|
<option value="openai">OpenAI</option>
|
|
951
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
952
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
953
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
945
954
|
<option value="gemini">Gemini</option>
|
|
946
955
|
<option value="azure_openai">Azure OpenAI</option>
|
|
947
956
|
<option value="cohere">Cohere</option>
|
|
@@ -963,6 +972,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
963
972
|
<input type="password" id="cfgEmbApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
964
973
|
</div>
|
|
965
974
|
</div>
|
|
975
|
+
<div class="test-conn-row">
|
|
976
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('embedding')" id="testEmbBtn" data-i18n="settings.test">Test Connection</button>
|
|
977
|
+
<span class="test-result" id="testEmbResult"></span>
|
|
978
|
+
</div>
|
|
966
979
|
</div>
|
|
967
980
|
|
|
968
981
|
<div class="settings-section">
|
|
@@ -970,9 +983,14 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
970
983
|
<div class="settings-grid">
|
|
971
984
|
<div class="settings-field">
|
|
972
985
|
<label data-i18n="settings.provider">Provider</label>
|
|
973
|
-
<select id="cfgSumProvider">
|
|
986
|
+
<select id="cfgSumProvider" onchange="onProviderChange('summarizer')">
|
|
974
987
|
<option value="openai_compatible">OpenAI Compatible</option>
|
|
975
988
|
<option value="openai">OpenAI</option>
|
|
989
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
990
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
991
|
+
<option value="deepseek">DeepSeek</option>
|
|
992
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
993
|
+
<option value="moonshot">Moonshot (Kimi)</option>
|
|
976
994
|
<option value="anthropic">Anthropic</option>
|
|
977
995
|
<option value="gemini">Gemini</option>
|
|
978
996
|
<option value="azure_openai">Azure OpenAI</option>
|
|
@@ -996,6 +1014,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
996
1014
|
<input type="number" id="cfgSumTemp" step="0.1" min="0" max="2" placeholder="0">
|
|
997
1015
|
</div>
|
|
998
1016
|
</div>
|
|
1017
|
+
<div class="test-conn-row">
|
|
1018
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('summarizer')" id="testSumBtn" data-i18n="settings.test">Test Connection</button>
|
|
1019
|
+
<span class="test-result" id="testSumResult"></span>
|
|
1020
|
+
</div>
|
|
999
1021
|
</div>
|
|
1000
1022
|
</div>
|
|
1001
1023
|
|
|
@@ -1025,10 +1047,15 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1025
1047
|
<div class="settings-grid">
|
|
1026
1048
|
<div class="settings-field">
|
|
1027
1049
|
<label data-i18n="settings.provider">Provider</label>
|
|
1028
|
-
<select id="cfgSkillProvider">
|
|
1050
|
+
<select id="cfgSkillProvider" onchange="onProviderChange('skill')">
|
|
1029
1051
|
<option value="">— <span data-i18n="settings.skill.usemain">Use main summarizer</span> —</option>
|
|
1030
1052
|
<option value="openai_compatible">OpenAI Compatible</option>
|
|
1031
1053
|
<option value="openai">OpenAI</option>
|
|
1054
|
+
<option value="siliconflow">SiliconFlow (\u7845\u57FA\u6D41\u52A8)</option>
|
|
1055
|
+
<option value="zhipu">Zhipu AI (\u667A\u8C31)</option>
|
|
1056
|
+
<option value="deepseek">DeepSeek</option>
|
|
1057
|
+
<option value="bailian">Alibaba Bailian (\u767E\u70BC)</option>
|
|
1058
|
+
<option value="moonshot">Moonshot (Kimi)</option>
|
|
1032
1059
|
<option value="anthropic">Anthropic</option>
|
|
1033
1060
|
<option value="gemini">Gemini</option>
|
|
1034
1061
|
<option value="azure_openai">Azure OpenAI</option>
|
|
@@ -1048,6 +1075,10 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1048
1075
|
<input type="password" id="cfgSkillApiKey" placeholder="\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022">
|
|
1049
1076
|
</div>
|
|
1050
1077
|
</div>
|
|
1078
|
+
<div class="test-conn-row">
|
|
1079
|
+
<button class="btn btn-sm btn-ghost" onclick="testModel('skill')" id="testSkillBtn" data-i18n="settings.test">Test Connection</button>
|
|
1080
|
+
<span class="test-result" id="testSkillResult"></span>
|
|
1081
|
+
</div>
|
|
1051
1082
|
</div>
|
|
1052
1083
|
</div>
|
|
1053
1084
|
|
|
@@ -1460,11 +1491,24 @@ const I18N={
|
|
|
1460
1491
|
'settings.telemetry.hint':'Anonymous usage analytics to help improve the plugin. Only sends tool names, latencies, and version info. No memory content, queries, or personal data is ever sent.',
|
|
1461
1492
|
'settings.viewerport':'Viewer Port',
|
|
1462
1493
|
'settings.viewerport.hint':'Requires restart to take effect',
|
|
1494
|
+
'settings.test':'Test Connection',
|
|
1495
|
+
'settings.test.loading':'Testing...',
|
|
1496
|
+
'settings.test.ok':'Connected',
|
|
1497
|
+
'settings.test.fail':'Failed',
|
|
1463
1498
|
'settings.save':'Save Settings',
|
|
1464
1499
|
'settings.reset':'Reset',
|
|
1465
1500
|
'settings.saved':'Saved',
|
|
1466
1501
|
'settings.restart.hint':'Some changes require restarting the OpenClaw gateway to take effect.',
|
|
1467
1502
|
'settings.save.fail':'Failed to save settings',
|
|
1503
|
+
'settings.save.emb.required':'Embedding model is required. Please configure an embedding model before saving.',
|
|
1504
|
+
'settings.save.emb.fail':'Embedding model test failed, cannot save',
|
|
1505
|
+
'settings.save.sum.fail':'Summarizer model test failed, cannot save',
|
|
1506
|
+
'settings.save.skill.fail':'Skill model test failed, cannot save',
|
|
1507
|
+
'settings.save.sum.fallback':'Summarizer model is not configured — will use OpenClaw native model as fallback.',
|
|
1508
|
+
'settings.save.skill.fallback':'Skill dedicated model is not configured — will use OpenClaw native model as fallback.',
|
|
1509
|
+
'settings.save.fallback.model':'Fallback model: ',
|
|
1510
|
+
'settings.save.fallback.none':'Not available (no OpenClaw native model found)',
|
|
1511
|
+
'settings.save.fallback.confirm':'Continue to save?',
|
|
1468
1512
|
'migrate.title':'Import OpenClaw Memory',
|
|
1469
1513
|
'migrate.desc':'Migrate your existing OpenClaw built-in memories and conversation history into this plugin. The import process uses smart deduplication to avoid duplicates.',
|
|
1470
1514
|
'migrate.modes.title':'Three ways to use:',
|
|
@@ -1571,7 +1615,10 @@ const I18N={
|
|
|
1571
1615
|
'skill.cancel':'Cancel',
|
|
1572
1616
|
'skill.delete.confirm':'Are you sure you want to delete this skill? This will also remove all associated files and cannot be undone.',
|
|
1573
1617
|
'skill.delete.error':'Failed to delete skill: ',
|
|
1574
|
-
'skill.save.error':'Failed to save skill: '
|
|
1618
|
+
'skill.save.error':'Failed to save skill: ',
|
|
1619
|
+
'update.available':'New version available',
|
|
1620
|
+
'update.run':'Run',
|
|
1621
|
+
'update.dismiss':'Dismiss'
|
|
1575
1622
|
},
|
|
1576
1623
|
zh:{
|
|
1577
1624
|
'title':'OpenClaw 记忆',
|
|
@@ -1753,11 +1800,24 @@ const I18N={
|
|
|
1753
1800
|
'settings.telemetry.hint':'匿名使用统计,帮助改进插件。仅发送工具名称、响应时间和版本信息,不会发送任何记忆内容、搜索查询或个人数据。',
|
|
1754
1801
|
'settings.viewerport':'Viewer 端口',
|
|
1755
1802
|
'settings.viewerport.hint':'修改后需重启网关生效',
|
|
1803
|
+
'settings.test':'测试连接',
|
|
1804
|
+
'settings.test.loading':'测试中...',
|
|
1805
|
+
'settings.test.ok':'连接成功',
|
|
1806
|
+
'settings.test.fail':'连接失败',
|
|
1756
1807
|
'settings.save':'保存设置',
|
|
1757
1808
|
'settings.reset':'重置',
|
|
1758
1809
|
'settings.saved':'已保存',
|
|
1759
1810
|
'settings.restart.hint':'部分设置修改后需要重启 OpenClaw 网关才能生效。',
|
|
1760
1811
|
'settings.save.fail':'保存设置失败',
|
|
1812
|
+
'settings.save.emb.required':'嵌入模型为必填项,请先配置嵌入模型再保存。',
|
|
1813
|
+
'settings.save.emb.fail':'嵌入模型测试失败,无法保存',
|
|
1814
|
+
'settings.save.sum.fail':'摘要模型测试失败,无法保存',
|
|
1815
|
+
'settings.save.skill.fail':'技能模型测试失败,无法保存',
|
|
1816
|
+
'settings.save.sum.fallback':'摘要模型未配置 — 将使用 OpenClaw 原生模型作为降级方案。',
|
|
1817
|
+
'settings.save.skill.fallback':'技能专用模型未配置 — 将使用 OpenClaw 原生模型作为降级方案。',
|
|
1818
|
+
'settings.save.fallback.model':'降级模型:',
|
|
1819
|
+
'settings.save.fallback.none':'不可用(未检测到 OpenClaw 原生模型)',
|
|
1820
|
+
'settings.save.fallback.confirm':'是否继续保存?',
|
|
1761
1821
|
'migrate.title':'导入 OpenClaw 记忆',
|
|
1762
1822
|
'migrate.desc':'将 OpenClaw 内置的记忆数据和对话历史迁移到本插件中。导入过程使用智能去重,避免重复导入。',
|
|
1763
1823
|
'migrate.modes.title':'三种使用方式:',
|
|
@@ -1864,7 +1924,10 @@ const I18N={
|
|
|
1864
1924
|
'skill.cancel':'取消',
|
|
1865
1925
|
'skill.delete.confirm':'确定要删除此技能吗?关联的文件也会被删除,此操作不可撤销。',
|
|
1866
1926
|
'skill.delete.error':'删除技能失败:',
|
|
1867
|
-
'skill.save.error':'保存技能失败:'
|
|
1927
|
+
'skill.save.error':'保存技能失败:',
|
|
1928
|
+
'update.available':'发现新版本',
|
|
1929
|
+
'update.run':'执行命令',
|
|
1930
|
+
'update.dismiss':'关闭'
|
|
1868
1931
|
}
|
|
1869
1932
|
};
|
|
1870
1933
|
const LANG_KEY='memos-viewer-lang';
|
|
@@ -2285,7 +2348,6 @@ async function loadTasks(){
|
|
|
2285
2348
|
'</div>'+
|
|
2286
2349
|
'<div class="card-actions" onclick="event.stopPropagation()">'+
|
|
2287
2350
|
'<button class="btn btn-sm btn-ghost" onclick="openTaskDetail(\\''+task.id+'\\')">'+t('card.expand')+'</button>'+
|
|
2288
|
-
'<button class="btn btn-sm btn-ghost" onclick="editTaskInline(\\''+task.id+'\\')">'+t('card.edit')+'</button>'+
|
|
2289
2351
|
(task.status==='completed'&&(!task.skillStatus||task.skillStatus==='not_generated'||task.skillStatus==='skipped')?'<button class="btn btn-sm btn-ghost" onclick="retrySkillGen(\\''+task.id+'\\')">'+t('task.retrySkill.short')+'</button>':'')+
|
|
2290
2352
|
'<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteTask(\\''+task.id+'\\')">'+t('task.delete')+'</button>'+
|
|
2291
2353
|
'</div>'+
|
|
@@ -2458,32 +2520,6 @@ async function deleteTask(taskId){
|
|
|
2458
2520
|
}catch(e){ alert(t('task.delete.error')+e.message); }
|
|
2459
2521
|
}
|
|
2460
2522
|
|
|
2461
|
-
async function editTaskInline(){
|
|
2462
|
-
if(!_currentTaskData) return;
|
|
2463
|
-
var task=_currentTaskData;
|
|
2464
|
-
var titleEl=document.getElementById('taskDetailTitle');
|
|
2465
|
-
var summaryEl=document.getElementById('taskDetailSummary');
|
|
2466
|
-
var actionsEl=document.getElementById('taskDetailActions');
|
|
2467
|
-
|
|
2468
|
-
titleEl.innerHTML='<input id="editTaskTitle" class="filter-input" style="width:100%;font-size:16px;font-weight:600" value="'+esc(task.title||'')+'"/>';
|
|
2469
|
-
summaryEl.innerHTML='<textarea id="editTaskSummary" class="filter-input" style="width:100%;min-height:80px;font-size:13px;resize:vertical">'+esc(task.summary||'')+'</textarea>';
|
|
2470
|
-
actionsEl.innerHTML=
|
|
2471
|
-
'<button class="btn btn-primary" onclick="saveTaskEdit()" style="font-size:12px">'+t('task.save')+'</button>'+
|
|
2472
|
-
'<button class="btn btn-ghost" onclick="openTaskDetail(\\''+esc(task.id)+'\\')" style="font-size:12px">'+t('task.cancel')+'</button>';
|
|
2473
|
-
}
|
|
2474
|
-
|
|
2475
|
-
async function saveTaskEdit(){
|
|
2476
|
-
if(!_currentTaskId) return;
|
|
2477
|
-
var title=document.getElementById('editTaskTitle').value.trim();
|
|
2478
|
-
var summary=document.getElementById('editTaskSummary').value.trim();
|
|
2479
|
-
try{
|
|
2480
|
-
const r=await fetch('/api/task/'+_currentTaskId,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({title:title,summary:summary})});
|
|
2481
|
-
const d=await r.json();
|
|
2482
|
-
if(!r.ok) throw new Error(d.error||'unknown');
|
|
2483
|
-
openTaskDetail(_currentTaskId);
|
|
2484
|
-
loadTasks();
|
|
2485
|
-
}catch(e){ alert(t('task.save.error')+e.message); }
|
|
2486
|
-
}
|
|
2487
2523
|
|
|
2488
2524
|
/* ─── Skills View Logic ─── */
|
|
2489
2525
|
let skillsStatusFilter='';
|
|
@@ -2544,7 +2580,6 @@ async function loadSkills(){
|
|
|
2544
2580
|
'</div>'+
|
|
2545
2581
|
'<div class="card-actions" onclick="event.stopPropagation()">'+
|
|
2546
2582
|
'<button class="btn btn-sm btn-ghost" onclick="openSkillDetail(\\''+skill.id+'\\')">'+t('card.expand')+'</button>'+
|
|
2547
|
-
'<button class="btn btn-sm btn-ghost" onclick="editSkillInline(\\''+skill.id+'\\')">'+t('card.edit')+'</button>'+
|
|
2548
2583
|
(skill.visibility==='public'?'<button class="btn btn-sm btn-ghost" onclick="toggleSkillPublic(\\''+skill.id+'\\',false)">\\u{1F512} '+t('skills.setPrivate')+'</button>':'<button class="btn btn-sm btn-ghost" onclick="toggleSkillPublic(\\''+skill.id+'\\',true)">\\u{1F310} '+t('skills.setPublic')+'</button>')+
|
|
2549
2584
|
'<button class="btn btn-sm btn-ghost" style="color:var(--accent)" onclick="deleteSkill(\\''+skill.id+'\\')">'+t('skill.delete')+'</button>'+
|
|
2550
2585
|
'</div>'+
|
|
@@ -2691,11 +2726,11 @@ async function toggleSkillVisibility(){
|
|
|
2691
2726
|
const newVis=btn.dataset.vis==='public'?'private':'public';
|
|
2692
2727
|
try{
|
|
2693
2728
|
const r=await fetch('/api/skill/'+currentSkillId+'/visibility',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({visibility:newVis})});
|
|
2694
|
-
if(!r.ok) throw new Error('
|
|
2729
|
+
if(!r.ok){var errBody='';try{var ej=await r.json();errBody=ej.error||JSON.stringify(ej);}catch(x){errBody=await r.text();}throw new Error(r.status+': '+errBody);}
|
|
2695
2730
|
openSkillDetail(currentSkillId);
|
|
2696
2731
|
loadSkills();
|
|
2697
2732
|
}catch(e){
|
|
2698
|
-
|
|
2733
|
+
toast('Error: '+e.message,'error');
|
|
2699
2734
|
}
|
|
2700
2735
|
}
|
|
2701
2736
|
|
|
@@ -2703,7 +2738,7 @@ async function toggleSkillPublic(id,setPublic){
|
|
|
2703
2738
|
const newVis=setPublic?'public':'private';
|
|
2704
2739
|
try{
|
|
2705
2740
|
const r=await fetch('/api/skill/'+id+'/visibility',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({visibility:newVis})});
|
|
2706
|
-
if(!r.ok) throw new Error('
|
|
2741
|
+
if(!r.ok){var errBody='';try{var ej=await r.json();errBody=ej.error||JSON.stringify(ej);}catch(x){errBody=await r.text();}throw new Error(r.status+': '+errBody);}
|
|
2707
2742
|
toast(setPublic?t('toast.setPublic'):t('toast.setPrivate'),'success');
|
|
2708
2743
|
loadSkills();
|
|
2709
2744
|
}catch(e){
|
|
@@ -2751,7 +2786,37 @@ async function loadConfig(){
|
|
|
2751
2786
|
}
|
|
2752
2787
|
}
|
|
2753
2788
|
|
|
2789
|
+
var _providerDefaults={
|
|
2790
|
+
siliconflow:{endpoint:'https://api.siliconflow.cn/v1',embModel:'BAAI/bge-m3',chatModel:'Qwen/Qwen2.5-7B-Instruct'},
|
|
2791
|
+
openai:{endpoint:'https://api.openai.com/v1',embModel:'text-embedding-3-small',chatModel:'gpt-4o-mini'},
|
|
2792
|
+
anthropic:{endpoint:'https://api.anthropic.com/v1/messages',chatModel:'claude-3-haiku-20240307'},
|
|
2793
|
+
cohere:{endpoint:'https://api.cohere.com/v2',embModel:'embed-english-v3.0'},
|
|
2794
|
+
mistral:{endpoint:'https://api.mistral.ai/v1',embModel:'mistral-embed'},
|
|
2795
|
+
voyage:{endpoint:'https://api.voyageai.com/v1',embModel:'voyage-3'},
|
|
2796
|
+
gemini:{endpoint:'',embModel:'text-embedding-004',chatModel:'gemini-2.0-flash'},
|
|
2797
|
+
zhipu:{endpoint:'https://open.bigmodel.cn/api/paas/v4',embModel:'embedding-3',chatModel:'glm-4-flash'},
|
|
2798
|
+
deepseek:{endpoint:'https://api.deepseek.com/v1',chatModel:'deepseek-chat'},
|
|
2799
|
+
bailian:{endpoint:'https://dashscope.aliyuncs.com/compatible-mode/v1',embModel:'text-embedding-v3',chatModel:'qwen-max'},
|
|
2800
|
+
moonshot:{endpoint:'https://api.moonshot.cn/v1',chatModel:'moonshot-v1-8k'}
|
|
2801
|
+
};
|
|
2802
|
+
function onProviderChange(section){
|
|
2803
|
+
var map={embedding:['cfgEmbEndpoint','cfgEmbModel','emb'],summarizer:['cfgSumEndpoint','cfgSumModel','chat'],skill:['cfgSkillEndpoint','cfgSkillModel','chat']};
|
|
2804
|
+
var m=map[section];if(!m)return;
|
|
2805
|
+
var sel=document.getElementById(section==='embedding'?'cfgEmbProvider':section==='summarizer'?'cfgSumProvider':'cfgSkillProvider');
|
|
2806
|
+
var pv=sel.value;
|
|
2807
|
+
var def=_providerDefaults[pv];
|
|
2808
|
+
if(!def)return;
|
|
2809
|
+
var epEl=document.getElementById(m[0]);
|
|
2810
|
+
var mdEl=document.getElementById(m[1]);
|
|
2811
|
+
if(def.endpoint&&!epEl.value.trim()) epEl.value=def.endpoint;
|
|
2812
|
+
if(m[2]==='emb'&&def.embModel&&!mdEl.value.trim()) mdEl.value=def.embModel;
|
|
2813
|
+
if(m[2]==='chat'&&def.chatModel&&!mdEl.value.trim()) mdEl.value=def.chatModel;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2754
2816
|
async function saveConfig(){
|
|
2817
|
+
var saveBtn=document.querySelector('.settings-actions .btn-primary');
|
|
2818
|
+
saveBtn.disabled=true;saveBtn.textContent=t('settings.test.loading');
|
|
2819
|
+
|
|
2755
2820
|
const cfg={};
|
|
2756
2821
|
const embP=document.getElementById('cfgEmbProvider').value;
|
|
2757
2822
|
if(embP){
|
|
@@ -2761,11 +2826,15 @@ async function saveConfig(){
|
|
|
2761
2826
|
const k=document.getElementById('cfgEmbApiKey').value.trim();if(k) cfg.embedding.apiKey=k;
|
|
2762
2827
|
}
|
|
2763
2828
|
const sumP=document.getElementById('cfgSumProvider').value;
|
|
2764
|
-
|
|
2829
|
+
const sumModel=document.getElementById('cfgSumModel').value.trim();
|
|
2830
|
+
const sumEndpoint=document.getElementById('cfgSumEndpoint').value.trim();
|
|
2831
|
+
const sumApiKey=document.getElementById('cfgSumApiKey').value.trim();
|
|
2832
|
+
var hasSumConfig=!!(sumModel||sumEndpoint||sumApiKey);
|
|
2833
|
+
if(hasSumConfig&&sumP){
|
|
2765
2834
|
cfg.summarizer={provider:sumP};
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2835
|
+
if(sumModel) cfg.summarizer.model=sumModel;
|
|
2836
|
+
if(sumEndpoint) cfg.summarizer.endpoint=sumEndpoint;
|
|
2837
|
+
if(sumApiKey) cfg.summarizer.apiKey=sumApiKey;
|
|
2769
2838
|
const tp=document.getElementById('cfgSumTemp').value.trim();if(tp!=='') cfg.summarizer.temperature=Number(tp);
|
|
2770
2839
|
}
|
|
2771
2840
|
cfg.skillEvolution={
|
|
@@ -2776,29 +2845,118 @@ async function saveConfig(){
|
|
|
2776
2845
|
const mk=document.getElementById('cfgSkillMinChunks').value.trim();if(mk) cfg.skillEvolution.minChunksForEval=Number(mk);
|
|
2777
2846
|
|
|
2778
2847
|
const skP=document.getElementById('cfgSkillProvider').value;
|
|
2779
|
-
|
|
2848
|
+
const skModel=document.getElementById('cfgSkillModel').value.trim();
|
|
2849
|
+
const skEndpoint=document.getElementById('cfgSkillEndpoint').value.trim();
|
|
2850
|
+
const skApiKey=document.getElementById('cfgSkillApiKey').value.trim();
|
|
2851
|
+
var hasSkillConfig=!!(skP&&(skModel||skEndpoint||skApiKey));
|
|
2852
|
+
if(hasSkillConfig){
|
|
2780
2853
|
cfg.skillEvolution.summarizer={provider:skP};
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2854
|
+
if(skModel) cfg.skillEvolution.summarizer.model=skModel;
|
|
2855
|
+
if(skEndpoint) cfg.skillEvolution.summarizer.endpoint=skEndpoint;
|
|
2856
|
+
if(skApiKey) cfg.skillEvolution.summarizer.apiKey=skApiKey;
|
|
2784
2857
|
}
|
|
2785
2858
|
|
|
2786
2859
|
const vp=document.getElementById('cfgViewerPort').value.trim();
|
|
2787
2860
|
if(vp) cfg.viewerPort=Number(vp);
|
|
2861
|
+
cfg.telemetry={enabled:document.getElementById('cfgTelemetryEnabled').checked};
|
|
2788
2862
|
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2863
|
+
function done(){saveBtn.disabled=false;saveBtn.textContent=t('settings.save');}
|
|
2864
|
+
|
|
2865
|
+
// 1) Embedding model is required
|
|
2866
|
+
if(!embP||embP===''){done();toast(t('settings.save.emb.required'),'error');return;}
|
|
2867
|
+
|
|
2868
|
+
// 2) Test embedding
|
|
2869
|
+
try{
|
|
2870
|
+
var er=await fetch('/api/test-model',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({type:'embedding',provider:cfg.embedding.provider,model:cfg.embedding.model||'',endpoint:cfg.embedding.endpoint||'',apiKey:cfg.embedding.apiKey||''})});
|
|
2871
|
+
var ed=await er.json();
|
|
2872
|
+
if(!ed.ok){done();toast(t('settings.save.emb.fail')+': '+ed.error,'error');document.getElementById('testEmbResult').className='test-result fail';document.getElementById('testEmbResult').innerHTML='\\u274C '+ed.error;return;}
|
|
2873
|
+
document.getElementById('testEmbResult').className='test-result ok';document.getElementById('testEmbResult').innerHTML='\\u2705 '+t('settings.test.ok');
|
|
2874
|
+
}catch(e){done();toast(t('settings.save.emb.fail')+': '+e.message,'error');return;}
|
|
2875
|
+
|
|
2876
|
+
// 3) Test summarizer if user filled it
|
|
2877
|
+
if(hasSumConfig&&cfg.summarizer){
|
|
2878
|
+
try{
|
|
2879
|
+
var sr=await fetch('/api/test-model',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({type:'summarizer',provider:cfg.summarizer.provider,model:cfg.summarizer.model||'',endpoint:cfg.summarizer.endpoint||'',apiKey:cfg.summarizer.apiKey||''})});
|
|
2880
|
+
var sd=await sr.json();
|
|
2881
|
+
if(!sd.ok){done();toast(t('settings.save.sum.fail')+': '+sd.error,'error');document.getElementById('testSumResult').className='test-result fail';document.getElementById('testSumResult').innerHTML='\\u274C '+sd.error;return;}
|
|
2882
|
+
document.getElementById('testSumResult').className='test-result ok';document.getElementById('testSumResult').innerHTML='\\u2705 '+t('settings.test.ok');
|
|
2883
|
+
}catch(e){done();toast(t('settings.save.sum.fail')+': '+e.message,'error');return;}
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
// 4) Test skill model if user filled it
|
|
2887
|
+
if(hasSkillConfig&&cfg.skillEvolution.summarizer){
|
|
2888
|
+
try{
|
|
2889
|
+
var kr=await fetch('/api/test-model',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({type:'summarizer',provider:cfg.skillEvolution.summarizer.provider,model:cfg.skillEvolution.summarizer.model||'',endpoint:cfg.skillEvolution.summarizer.endpoint||'',apiKey:cfg.skillEvolution.summarizer.apiKey||''})});
|
|
2890
|
+
var kd=await kr.json();
|
|
2891
|
+
if(!kd.ok){done();toast(t('settings.save.skill.fail')+': '+kd.error,'error');document.getElementById('testSkillResult').className='test-result fail';document.getElementById('testSkillResult').innerHTML='\\u274C '+kd.error;return;}
|
|
2892
|
+
document.getElementById('testSkillResult').className='test-result ok';document.getElementById('testSkillResult').innerHTML='\\u2705 '+t('settings.test.ok');
|
|
2893
|
+
}catch(e){done();toast(t('settings.save.skill.fail')+': '+e.message,'error');return;}
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
// 5) If summarizer or skill model not configured, check OpenClaw fallback and confirm
|
|
2897
|
+
if(!hasSumConfig||!hasSkillConfig){
|
|
2898
|
+
try{
|
|
2899
|
+
var fr=await fetch('/api/fallback-model');
|
|
2900
|
+
var fb=await fr.json();
|
|
2901
|
+
var msgs=[];
|
|
2902
|
+
if(!hasSumConfig){msgs.push(t('settings.save.sum.fallback'));}
|
|
2903
|
+
if(!hasSkillConfig){msgs.push(t('settings.save.skill.fallback'));}
|
|
2904
|
+
var fbInfo=fb.available?(fb.model+' ('+fb.baseUrl+')'):t('settings.save.fallback.none');
|
|
2905
|
+
var confirmMsg=msgs.join('\\n')+'\\n\\n'+t('settings.save.fallback.model')+fbInfo+'\\n\\n'+t('settings.save.fallback.confirm');
|
|
2906
|
+
if(!confirm(confirmMsg)){done();return;}
|
|
2907
|
+
}catch(e){}
|
|
2908
|
+
}
|
|
2792
2909
|
|
|
2910
|
+
// 6) All tests passed, save
|
|
2793
2911
|
try{
|
|
2794
2912
|
const r=await fetch('/api/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(cfg)});
|
|
2795
2913
|
if(!r.ok) throw new Error(await r.text());
|
|
2796
2914
|
const el=document.getElementById('settingsSaved');
|
|
2797
2915
|
el.classList.add('show');
|
|
2798
2916
|
setTimeout(()=>el.classList.remove('show'),2500);
|
|
2917
|
+
toast(t('settings.saved'),'success');
|
|
2799
2918
|
}catch(e){
|
|
2800
|
-
|
|
2919
|
+
toast(t('settings.save.fail')+': '+e.message,'error');
|
|
2920
|
+
}finally{done();}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
async function testModel(type){
|
|
2924
|
+
var ids={embedding:['Emb','cfgEmbProvider','cfgEmbModel','cfgEmbEndpoint','cfgEmbApiKey'],summarizer:['Sum','cfgSumProvider','cfgSumModel','cfgSumEndpoint','cfgSumApiKey'],skill:['Skill','cfgSkillProvider','cfgSkillModel','cfgSkillEndpoint','cfgSkillApiKey']};
|
|
2925
|
+
var c=ids[type];if(!c)return;
|
|
2926
|
+
var resultEl=document.getElementById('test'+c[0]+'Result');
|
|
2927
|
+
var btn=document.getElementById('test'+c[0]+'Btn');
|
|
2928
|
+
var provider=document.getElementById(c[1]).value;
|
|
2929
|
+
var model=document.getElementById(c[2]).value.trim();
|
|
2930
|
+
var endpoint=document.getElementById(c[3]).value.trim();
|
|
2931
|
+
var apiKey=document.getElementById(c[4]).value.trim();
|
|
2932
|
+
if(!provider||(provider!=='local'&&!model)){
|
|
2933
|
+
resultEl.className='test-result fail';
|
|
2934
|
+
resultEl.innerHTML='\\u274C '+t('settings.test.fail')+'<div style="margin-top:4px;font-size:11px;color:var(--text-muted)">Provider and Model are required</div>';
|
|
2935
|
+
return;
|
|
2801
2936
|
}
|
|
2937
|
+
if(provider!=='local'&&!apiKey){
|
|
2938
|
+
resultEl.className='test-result fail';
|
|
2939
|
+
resultEl.innerHTML='\\u274C '+t('settings.test.fail')+'<div style="margin-top:4px;font-size:11px;color:var(--text-muted)">API Key is required</div>';
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
resultEl.className='test-result loading';resultEl.textContent=t('settings.test.loading');
|
|
2943
|
+
btn.disabled=true;
|
|
2944
|
+
try{
|
|
2945
|
+
var body={type:type,provider:provider,model:model,endpoint:endpoint,apiKey:apiKey};
|
|
2946
|
+
var r=await fetch('/api/test-model',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
|
2947
|
+
var d=await r.json();
|
|
2948
|
+
if(d.ok){
|
|
2949
|
+
resultEl.className='test-result ok';
|
|
2950
|
+
resultEl.innerHTML='\\u2705 '+t('settings.test.ok')+'<div style="margin-top:4px;font-size:11px;color:var(--text-muted)">'+esc(d.detail||'')+'</div>';
|
|
2951
|
+
}else{
|
|
2952
|
+
var errMsg=d.error||'Unknown error';
|
|
2953
|
+
resultEl.className='test-result fail';
|
|
2954
|
+
resultEl.innerHTML='\\u274C '+t('settings.test.fail')+'<div style="margin-top:6px;font-size:11px;padding:8px 10px;background:rgba(239,68,68,.06);border:1px solid rgba(239,68,68,.15);border-radius:6px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow-y:auto;font-family:SF Mono,Monaco,Consolas,monospace">'+esc(errMsg)+'</div>';
|
|
2955
|
+
}
|
|
2956
|
+
}catch(e){
|
|
2957
|
+
resultEl.className='test-result fail';
|
|
2958
|
+
resultEl.innerHTML='\\u274C '+t('settings.test.fail')+'<div style="margin-top:6px;font-size:11px;padding:8px 10px;background:rgba(239,68,68,.06);border:1px solid rgba(239,68,68,.15);border-radius:6px;white-space:pre-wrap;word-break:break-all">'+esc(e.message)+'</div>';
|
|
2959
|
+
}finally{btn.disabled=false;}
|
|
2802
2960
|
}
|
|
2803
2961
|
|
|
2804
2962
|
function renderSkillMarkdown(md){
|
|
@@ -2844,28 +3002,6 @@ async function deleteSkill(skillId){
|
|
|
2844
3002
|
}catch(e){ alert(t('skill.delete.error')+e.message); }
|
|
2845
3003
|
}
|
|
2846
3004
|
|
|
2847
|
-
function editSkillInline(){
|
|
2848
|
-
var skill=window._currentSkillData;
|
|
2849
|
-
if(!skill) return;
|
|
2850
|
-
var descEl=document.getElementById('skillDetailDesc');
|
|
2851
|
-
var actionsEl=document.getElementById('skillDetailActions');
|
|
2852
|
-
descEl.innerHTML='<textarea id="editSkillDesc" class="filter-input" style="width:100%;min-height:60px;font-size:13px;resize:vertical">'+esc(skill.description||'')+'</textarea>';
|
|
2853
|
-
actionsEl.innerHTML=
|
|
2854
|
-
'<button class="btn btn-primary" onclick="saveSkillEdit()" style="font-size:12px">'+t('skill.save')+'</button>'+
|
|
2855
|
-
'<button class="btn btn-ghost" onclick="openSkillDetail(\\''+esc(skill.id)+'\\')" style="font-size:12px">'+t('skill.cancel')+'</button>';
|
|
2856
|
-
}
|
|
2857
|
-
|
|
2858
|
-
async function saveSkillEdit(){
|
|
2859
|
-
if(!currentSkillId) return;
|
|
2860
|
-
var desc=document.getElementById('editSkillDesc').value.trim();
|
|
2861
|
-
try{
|
|
2862
|
-
const r=await fetch('/api/skill/'+currentSkillId,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({description:desc})});
|
|
2863
|
-
const d=await r.json();
|
|
2864
|
-
if(!r.ok) throw new Error(d.error||'unknown');
|
|
2865
|
-
openSkillDetail(currentSkillId);
|
|
2866
|
-
loadSkills();
|
|
2867
|
-
}catch(e){ alert(t('skill.save.error')+e.message); }
|
|
2868
|
-
}
|
|
2869
3005
|
|
|
2870
3006
|
function formatDuration(ms){
|
|
2871
3007
|
const s=Math.floor(ms/1000);
|
|
@@ -3147,6 +3283,7 @@ async function loadAll(){
|
|
|
3147
3283
|
await Promise.all([loadStats(),loadMemories()]);
|
|
3148
3284
|
checkMigrateStatus();
|
|
3149
3285
|
connectPPSSE();
|
|
3286
|
+
checkForUpdate();
|
|
3150
3287
|
}
|
|
3151
3288
|
|
|
3152
3289
|
async function loadStats(){
|
|
@@ -4083,6 +4220,22 @@ function initViewerTheme(){const s=localStorage.getItem(VIEWER_THEME_KEY);const
|
|
|
4083
4220
|
function toggleViewerTheme(){const el=document.documentElement;const cur=el.getAttribute('data-theme')||'dark';const next=cur==='dark'?'light':'dark';el.setAttribute('data-theme',next);localStorage.setItem(VIEWER_THEME_KEY,next);}
|
|
4084
4221
|
initViewerTheme();
|
|
4085
4222
|
|
|
4223
|
+
/* ─── Update check ─── */
|
|
4224
|
+
async function checkForUpdate(){
|
|
4225
|
+
try{
|
|
4226
|
+
const r=await fetch('/api/update-check');
|
|
4227
|
+
if(!r.ok)return;
|
|
4228
|
+
const d=await r.json();
|
|
4229
|
+
if(!d.updateAvailable)return;
|
|
4230
|
+
const banner=document.createElement('div');
|
|
4231
|
+
banner.id='updateBanner';
|
|
4232
|
+
banner.style.cssText='position:fixed;top:0;left:0;right:0;z-index:9999;background:linear-gradient(135deg,#f59e0b,#d97706);color:#fff;padding:10px 20px;display:flex;align-items:center;justify-content:space-between;font-size:14px;box-shadow:0 2px 8px rgba(0,0,0,.25)';
|
|
4233
|
+
banner.innerHTML='<span>🔔 '+t('update.available')+': <b>v'+esc(d.current)+'</b> → <b>v'+esc(d.latest)+'</b> — '+t('update.run')+': <code style="background:rgba(0,0,0,.2);padding:2px 8px;border-radius:4px;margin:0 4px">openclaw plugins install '+esc(d.packageName)+'</code></span><button onclick="this.parentElement.remove();document.body.style.paddingTop=\\'\\';" style="background:none;border:none;color:#fff;font-size:18px;cursor:pointer;padding:0 4px">×</button>';
|
|
4234
|
+
document.body.prepend(banner);
|
|
4235
|
+
document.body.style.paddingTop='48px';
|
|
4236
|
+
}catch(e){}
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4086
4239
|
/* ─── Init ─── */
|
|
4087
4240
|
document.getElementById('modalOverlay').addEventListener('click',e=>{if(e.target.id==='modalOverlay')closeModal()});
|
|
4088
4241
|
document.getElementById('searchInput').addEventListener('keydown',e=>{if(e.key==='Escape'){e.target.value='';loadMemories()}});
|