@memtensor/memos-local-openclaw-plugin 1.0.2-beta.6 → 1.0.2-beta.8
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/dist/embedding/index.d.ts.map +1 -1
- package/dist/embedding/index.js +4 -3
- package/dist/embedding/index.js.map +1 -1
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +39 -2
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/recall/engine.js +1 -1
- package/dist/recall/engine.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/update-check.d.ts.map +1 -1
- package/dist/update-check.js +0 -1
- package/dist/update-check.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +73 -37
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +116 -18
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +1 -1
- package/package.json +4 -3
- package/src/embedding/index.ts +4 -2
- package/src/ingest/providers/index.ts +39 -2
- package/src/recall/engine.ts +1 -1
- package/src/types.ts +7 -1
- package/src/update-check.ts +0 -1
- package/src/viewer/html.ts +73 -37
- package/src/viewer/server.ts +102 -16
|
@@ -186,8 +186,9 @@ export class Summarizer {
|
|
|
186
186
|
return resultCleaned;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
if (resultCleaned !== undefined) {
|
|
190
|
-
|
|
189
|
+
if (resultCleaned !== undefined && resultCleaned !== null) {
|
|
190
|
+
const len: number = (resultCleaned as string).length;
|
|
191
|
+
this.log.warn(`summarize: result (${len}) >= input (${cleaned.length}), retrying`);
|
|
191
192
|
}
|
|
192
193
|
} catch (err) {
|
|
193
194
|
this.log.warn(`summarize primary failed: ${err}`);
|
|
@@ -282,6 +283,12 @@ function callSummarize(cfg: SummarizerConfig, text: string, log: Logger): Promis
|
|
|
282
283
|
case "openai":
|
|
283
284
|
case "openai_compatible":
|
|
284
285
|
case "azure_openai":
|
|
286
|
+
case "zhipu":
|
|
287
|
+
case "siliconflow":
|
|
288
|
+
case "bailian":
|
|
289
|
+
case "cohere":
|
|
290
|
+
case "mistral":
|
|
291
|
+
case "voyage":
|
|
285
292
|
return summarizeOpenAI(text, cfg, log);
|
|
286
293
|
case "anthropic":
|
|
287
294
|
return summarizeAnthropic(text, cfg, log);
|
|
@@ -299,6 +306,12 @@ function callSummarizeTask(cfg: SummarizerConfig, text: string, log: Logger): Pr
|
|
|
299
306
|
case "openai":
|
|
300
307
|
case "openai_compatible":
|
|
301
308
|
case "azure_openai":
|
|
309
|
+
case "zhipu":
|
|
310
|
+
case "siliconflow":
|
|
311
|
+
case "bailian":
|
|
312
|
+
case "cohere":
|
|
313
|
+
case "mistral":
|
|
314
|
+
case "voyage":
|
|
302
315
|
return summarizeTaskOpenAI(text, cfg, log);
|
|
303
316
|
case "anthropic":
|
|
304
317
|
return summarizeTaskAnthropic(text, cfg, log);
|
|
@@ -316,6 +329,12 @@ function callGenerateTaskTitle(cfg: SummarizerConfig, text: string, log: Logger)
|
|
|
316
329
|
case "openai":
|
|
317
330
|
case "openai_compatible":
|
|
318
331
|
case "azure_openai":
|
|
332
|
+
case "zhipu":
|
|
333
|
+
case "siliconflow":
|
|
334
|
+
case "bailian":
|
|
335
|
+
case "cohere":
|
|
336
|
+
case "mistral":
|
|
337
|
+
case "voyage":
|
|
319
338
|
return generateTaskTitleOpenAI(text, cfg, log);
|
|
320
339
|
case "anthropic":
|
|
321
340
|
return generateTaskTitleAnthropic(text, cfg, log);
|
|
@@ -333,6 +352,12 @@ function callTopicJudge(cfg: SummarizerConfig, currentContext: string, newMessag
|
|
|
333
352
|
case "openai":
|
|
334
353
|
case "openai_compatible":
|
|
335
354
|
case "azure_openai":
|
|
355
|
+
case "zhipu":
|
|
356
|
+
case "siliconflow":
|
|
357
|
+
case "bailian":
|
|
358
|
+
case "cohere":
|
|
359
|
+
case "mistral":
|
|
360
|
+
case "voyage":
|
|
336
361
|
return judgeNewTopicOpenAI(currentContext, newMessage, cfg, log);
|
|
337
362
|
case "anthropic":
|
|
338
363
|
return judgeNewTopicAnthropic(currentContext, newMessage, cfg, log);
|
|
@@ -350,6 +375,12 @@ function callFilterRelevant(cfg: SummarizerConfig, query: string, candidates: Ar
|
|
|
350
375
|
case "openai":
|
|
351
376
|
case "openai_compatible":
|
|
352
377
|
case "azure_openai":
|
|
378
|
+
case "zhipu":
|
|
379
|
+
case "siliconflow":
|
|
380
|
+
case "bailian":
|
|
381
|
+
case "cohere":
|
|
382
|
+
case "mistral":
|
|
383
|
+
case "voyage":
|
|
353
384
|
return filterRelevantOpenAI(query, candidates, cfg, log);
|
|
354
385
|
case "anthropic":
|
|
355
386
|
return filterRelevantAnthropic(query, candidates, cfg, log);
|
|
@@ -367,6 +398,12 @@ function callJudgeDedup(cfg: SummarizerConfig, newSummary: string, candidates: A
|
|
|
367
398
|
case "openai":
|
|
368
399
|
case "openai_compatible":
|
|
369
400
|
case "azure_openai":
|
|
401
|
+
case "zhipu":
|
|
402
|
+
case "siliconflow":
|
|
403
|
+
case "bailian":
|
|
404
|
+
case "cohere":
|
|
405
|
+
case "mistral":
|
|
406
|
+
case "voyage":
|
|
370
407
|
return judgeDedupOpenAI(newSummary, candidates, cfg, log);
|
|
371
408
|
case "anthropic":
|
|
372
409
|
return judgeDedupAnthropic(newSummary, candidates, cfg, log);
|
package/src/recall/engine.ts
CHANGED
|
@@ -270,8 +270,8 @@ export class RecallEngine {
|
|
|
270
270
|
): Promise<number[]> {
|
|
271
271
|
const candidateList = candidates.map((c, i) => ({
|
|
272
272
|
index: i,
|
|
273
|
-
content: `[${c.skill.name}] ${c.skill.description}`,
|
|
274
273
|
role: "skill" as const,
|
|
274
|
+
content: `[${c.skill.name}] ${c.skill.description}`,
|
|
275
275
|
}));
|
|
276
276
|
|
|
277
277
|
try {
|
package/src/types.ts
CHANGED
|
@@ -144,7 +144,13 @@ export type SummaryProvider =
|
|
|
144
144
|
| "anthropic"
|
|
145
145
|
| "gemini"
|
|
146
146
|
| "azure_openai"
|
|
147
|
-
| "bedrock"
|
|
147
|
+
| "bedrock"
|
|
148
|
+
| "zhipu"
|
|
149
|
+
| "siliconflow"
|
|
150
|
+
| "bailian"
|
|
151
|
+
| "cohere"
|
|
152
|
+
| "mistral"
|
|
153
|
+
| "voyage";
|
|
148
154
|
|
|
149
155
|
export type EmbeddingProvider =
|
|
150
156
|
| "openai"
|
package/src/update-check.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* - Stable users compare against latest tag only (semver gt).
|
|
5
5
|
* - Beta users get optional stableChannel hint to install @latest when stable exists.
|
|
6
6
|
*/
|
|
7
|
-
// @ts-ignore — semver has no bundled types
|
|
8
7
|
import * as semver from "semver";
|
|
9
8
|
|
|
10
9
|
export interface UpdateCheckResult {
|
package/src/viewer/html.ts
CHANGED
|
@@ -586,7 +586,7 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
586
586
|
[data-theme="light"] .settings-actions .btn-primary:hover{background:rgba(79,70,229,.1);border-color:#4f46e5}
|
|
587
587
|
.settings-saved{display:inline-flex;align-items:center;gap:6px;color:var(--green);font-size:12px;font-weight:600;opacity:0;transition:opacity .3s}
|
|
588
588
|
.settings-saved.show{opacity:1}
|
|
589
|
-
.model-health-bar{margin-bottom:20px;border-radius:var(--radius-lg);overflow:
|
|
589
|
+
.model-health-bar{margin-bottom:20px;border-radius:var(--radius-lg);overflow:visible}
|
|
590
590
|
.mh-table{width:100%;border-collapse:separate;border-spacing:0;font-size:12px}
|
|
591
591
|
.mh-table th{text-align:left;padding:6px 12px;font-size:10px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;background:var(--bg);border-bottom:1px solid var(--border)}
|
|
592
592
|
.mh-table td{padding:8px 12px;border-bottom:1px solid var(--border);vertical-align:middle}
|
|
@@ -604,7 +604,8 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
604
604
|
.mh-badge.error{background:rgba(239,68,68,.1);color:#dc2626}
|
|
605
605
|
.mh-badge.unknown{background:rgba(148,163,184,.1);color:#64748b}
|
|
606
606
|
.mh-model-name{color:var(--text-muted);font-size:11px;font-family:var(--font-mono,'SFMono-Regular',Consolas,monospace)}
|
|
607
|
-
.mh-err-text{font-size:11px;color:var(--rose);max-width:
|
|
607
|
+
.mh-err-text{font-size:11px;color:var(--rose);max-width:320px;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:help}
|
|
608
|
+
#mhTooltip{display:none;position:fixed;min-width:280px;max-width:480px;max-height:300px;overflow-y:auto;padding:8px 10px;background:var(--bg-card,#1e1e2e);color:var(--text,#e2e8f0);border:1px solid var(--border,#333);border-radius:6px;font-size:11px;line-height:1.5;white-space:pre-wrap;word-break:break-all;box-shadow:0 4px 12px rgba(0,0,0,.25);z-index:10000;pointer-events:none}
|
|
608
609
|
.mh-time{font-size:10px;color:var(--text-muted);white-space:nowrap}
|
|
609
610
|
.mh-empty{padding:16px;font-size:12px;color:var(--text-muted);text-align:center}
|
|
610
611
|
@keyframes healthPulse{0%,100%{opacity:1}50%{opacity:.4}}
|
|
@@ -1690,10 +1691,11 @@ const I18N={
|
|
|
1690
1691
|
'skill.save.error':'Failed to save skill: ',
|
|
1691
1692
|
'update.available':'New version available',
|
|
1692
1693
|
'update.run':'Run',
|
|
1693
|
-
'update.btn':'Update
|
|
1694
|
-
'update.installing':'Installing
|
|
1695
|
-
'update.success':'
|
|
1694
|
+
'update.btn':'Update',
|
|
1695
|
+
'update.installing':'Installing...',
|
|
1696
|
+
'update.success':'Updated!',
|
|
1696
1697
|
'update.failed':'Update failed',
|
|
1698
|
+
'update.restarting':'Restarting service...',
|
|
1697
1699
|
'update.dismiss':'Dismiss'
|
|
1698
1700
|
},
|
|
1699
1701
|
zh:{
|
|
@@ -2011,10 +2013,11 @@ const I18N={
|
|
|
2011
2013
|
'skill.save.error':'保存技能失败:',
|
|
2012
2014
|
'update.available':'发现新版本',
|
|
2013
2015
|
'update.run':'执行命令',
|
|
2014
|
-
'update.btn':'
|
|
2015
|
-
'update.installing':'
|
|
2016
|
-
'update.success':'
|
|
2016
|
+
'update.btn':'更新',
|
|
2017
|
+
'update.installing':'安装中...',
|
|
2018
|
+
'update.success':'更新完成',
|
|
2017
2019
|
'update.failed':'更新失败',
|
|
2020
|
+
'update.restarting':'正在重启服务...',
|
|
2018
2021
|
'update.dismiss':'关闭'
|
|
2019
2022
|
}
|
|
2020
2023
|
};
|
|
@@ -3008,7 +3011,14 @@ function classifyError(msg){
|
|
|
3008
3011
|
if(msg.indexOf('ECONNREFUSED')>=0) return 'Connection refused';
|
|
3009
3012
|
if(msg.indexOf('ENOTFOUND')>=0) return 'DNS resolution failed';
|
|
3010
3013
|
if(msg.indexOf('403')>=0) return 'Forbidden (403)';
|
|
3011
|
-
return
|
|
3014
|
+
if(msg.indexOf('503')>=0||msg.indexOf('upstream connect error')>=0||msg.indexOf('Service Unavailable')>=0) return 'Service unavailable (503)';
|
|
3015
|
+
if(msg.indexOf('502')>=0||msg.indexOf('Bad Gateway')>=0) return 'Bad gateway (502)';
|
|
3016
|
+
if(msg.indexOf('500')>=0||msg.indexOf('Internal Server Error')>=0) return 'Server error (500)';
|
|
3017
|
+
if(msg.indexOf('404')>=0||msg.indexOf('Not Found')>=0) return 'Not found (404)';
|
|
3018
|
+
if(msg.indexOf('fetch failed')>=0||msg.indexOf('ETIMEDOUT')>=0) return 'Network error';
|
|
3019
|
+
if(msg.indexOf('Unknown')>=0&&msg.indexOf('provider')>=0) return 'Unknown provider';
|
|
3020
|
+
var m=msg.match(/\((\d{3})\)/); if(m) return 'HTTP error ('+m[1]+')';
|
|
3021
|
+
return msg.length>80?msg.substring(0,77)+'...':msg;
|
|
3012
3022
|
}
|
|
3013
3023
|
|
|
3014
3024
|
function shortenModel(s){return s?s.replace('openai_compatible/','').replace('openai/',''):'\u2014';}
|
|
@@ -3054,7 +3064,7 @@ async function loadModelHealth(){
|
|
|
3054
3064
|
issue+=shortErr;
|
|
3055
3065
|
if(m.consecutiveErrors>1) issue+=' ('+m.consecutiveErrors+'x)';
|
|
3056
3066
|
}
|
|
3057
|
-
if(issue) h+='<td><span class="mh-err-text"
|
|
3067
|
+
if(issue) h+='<td><span class="mh-err-text" data-err="'+escapeHtml(m.lastErrorMessage||'')+'">'+escapeHtml(issue)+'</span></td>';
|
|
3058
3068
|
else h+='<td><span style="color:var(--text-muted);font-size:11px">\u2014</span></td>';
|
|
3059
3069
|
|
|
3060
3070
|
h+='<td style="text-align:right"><span class="mh-time">'+(ago||'\u2014')+'</span></td>';
|
|
@@ -3062,11 +3072,29 @@ async function loadModelHealth(){
|
|
|
3062
3072
|
}
|
|
3063
3073
|
h+='</tbody></table>';
|
|
3064
3074
|
bar.innerHTML=h;
|
|
3075
|
+
initMhTooltips();
|
|
3065
3076
|
}catch(e){
|
|
3066
3077
|
bar.innerHTML='<div class="mh-empty">Failed to load model health</div>';
|
|
3067
3078
|
}
|
|
3068
3079
|
}
|
|
3069
3080
|
|
|
3081
|
+
function initMhTooltips(){
|
|
3082
|
+
var tip=document.getElementById('mhTooltip');
|
|
3083
|
+
if(!tip){tip=document.createElement('div');tip.id='mhTooltip';document.body.appendChild(tip);}
|
|
3084
|
+
document.querySelectorAll('.mh-err-text[data-err]').forEach(function(el){
|
|
3085
|
+
el.addEventListener('mouseenter',function(e){
|
|
3086
|
+
var msg=el.getAttribute('data-err');
|
|
3087
|
+
if(!msg)return;
|
|
3088
|
+
tip.textContent=msg;
|
|
3089
|
+
tip.style.display='block';
|
|
3090
|
+
var rect=el.getBoundingClientRect();
|
|
3091
|
+
tip.style.left=Math.max(0,Math.min(rect.left,window.innerWidth-490))+'px';
|
|
3092
|
+
tip.style.top=(rect.bottom+6)+'px';
|
|
3093
|
+
});
|
|
3094
|
+
el.addEventListener('mouseleave',function(){tip.style.display='none';});
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3070
3098
|
function timeAgo(ts){
|
|
3071
3099
|
var diff=Date.now()-ts;
|
|
3072
3100
|
if(diff<60000) return 'just now';
|
|
@@ -4663,35 +4691,35 @@ function waitForGatewayAndReload(maxAttempts,attempt){
|
|
|
4663
4691
|
attempt=attempt||0;
|
|
4664
4692
|
if(attempt>=maxAttempts){window.location.reload();return;}
|
|
4665
4693
|
setTimeout(function(){
|
|
4666
|
-
fetch('/api/
|
|
4667
|
-
|
|
4668
|
-
else waitForGatewayAndReload(maxAttempts,attempt+1);
|
|
4694
|
+
fetch('/api/auth/status').then(function(){
|
|
4695
|
+
window.location.reload();
|
|
4669
4696
|
}).catch(function(){waitForGatewayAndReload(maxAttempts,attempt+1);});
|
|
4670
4697
|
},3000);
|
|
4671
4698
|
}
|
|
4672
|
-
function doUpdateInstall(packageSpec,btnEl){
|
|
4699
|
+
function doUpdateInstall(packageSpec,btnEl,statusEl){
|
|
4673
4700
|
btnEl.disabled=true;
|
|
4674
4701
|
btnEl.textContent=t('update.installing');
|
|
4702
|
+
btnEl.style.cssText='background:rgba(99,102,241,.15);color:var(--pri);border:1px solid rgba(99,102,241,.3);border-radius:6px;padding:4px 14px;font-size:12px;font-weight:600;cursor:wait;white-space:nowrap';
|
|
4675
4703
|
fetch('/api/update-install',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({packageSpec:packageSpec})})
|
|
4676
4704
|
.then(function(r){return r.json()})
|
|
4677
4705
|
.then(function(d){
|
|
4678
4706
|
if(d.ok){
|
|
4679
4707
|
btnEl.textContent=t('update.success');
|
|
4680
|
-
btnEl.style.
|
|
4681
|
-
|
|
4682
|
-
waitForGatewayAndReload(
|
|
4708
|
+
btnEl.style.cssText='background:rgba(34,197,94,.15);color:#22c55e;border:1px solid rgba(34,197,94,.3);border-radius:6px;padding:4px 14px;font-size:12px;font-weight:600;cursor:default;white-space:nowrap';
|
|
4709
|
+
if(statusEl)statusEl.textContent=t('update.restarting');
|
|
4710
|
+
waitForGatewayAndReload(40);
|
|
4683
4711
|
}else{
|
|
4684
|
-
btnEl.textContent=t('update.
|
|
4685
|
-
btnEl.style.
|
|
4686
|
-
btnEl.style.color='#fff';
|
|
4712
|
+
btnEl.textContent=t('update.btn');
|
|
4713
|
+
btnEl.style.cssText='background:none;border:1px solid currentColor;border-radius:6px;padding:4px 14px;font-size:12px;font-weight:600;color:inherit;cursor:pointer;white-space:nowrap;opacity:.85';
|
|
4687
4714
|
btnEl.disabled=false;
|
|
4688
|
-
|
|
4715
|
+
if(statusEl)statusEl.textContent=t('update.failed')+': '+(d.error||'').slice(0,60);
|
|
4716
|
+
setTimeout(function(){if(statusEl)statusEl.textContent='';},8000);
|
|
4689
4717
|
}
|
|
4690
4718
|
})
|
|
4691
|
-
.catch(function(
|
|
4692
|
-
btnEl.textContent=t('update.
|
|
4719
|
+
.catch(function(){
|
|
4720
|
+
btnEl.textContent=t('update.btn');
|
|
4721
|
+
btnEl.style.cssText='background:none;border:1px solid currentColor;border-radius:6px;padding:4px 14px;font-size:12px;font-weight:600;color:inherit;cursor:pointer;white-space:nowrap;opacity:.85';
|
|
4693
4722
|
btnEl.disabled=false;
|
|
4694
|
-
setTimeout(function(){btnEl.textContent=t('update.btn');btnEl.style.background='';btnEl.style.color='';},5000);
|
|
4695
4723
|
});
|
|
4696
4724
|
}
|
|
4697
4725
|
async function checkForUpdate(){
|
|
@@ -4700,25 +4728,33 @@ async function checkForUpdate(){
|
|
|
4700
4728
|
if(!r.ok)return;
|
|
4701
4729
|
const d=await r.json();
|
|
4702
4730
|
if(!d.updateAvailable)return;
|
|
4703
|
-
const pkgSpec=d.installCommand?d.installCommand.replace(/^openclaw
|
|
4704
|
-
|
|
4731
|
+
const pkgSpec=d.installCommand?d.installCommand.replace(/^(?:npx\s+)?openclaw\s+plugins\s+install\s+/,''):(d.packageName+'@'+d.latest);
|
|
4732
|
+
var banner=document.createElement('div');
|
|
4705
4733
|
banner.id='updateBanner';
|
|
4706
|
-
banner.style.cssText='
|
|
4707
|
-
var
|
|
4708
|
-
|
|
4734
|
+
banner.style.cssText='display:flex;align-items:center;gap:10px;padding:12px 20px;font-size:13px;font-weight:500;border-radius:10px;margin:0 32px;animation:slideIn .3s ease;background:rgba(245,158,11,.1);color:#d97706;border:1px solid rgba(245,158,11,.25)';
|
|
4735
|
+
var textNode=document.createElement('div');
|
|
4736
|
+
textNode.style.cssText='display:flex;align-items:center;gap:8px;flex-shrink:0';
|
|
4737
|
+
textNode.innerHTML='\u{1F4E6} '+t('update.available')+' <b style="margin:0 2px">v'+esc(d.current)+'</b> \u2192 <b style="margin:0 2px">v'+esc(d.latest)+'</b>';
|
|
4709
4738
|
var btnUpdate=document.createElement('button');
|
|
4739
|
+
btnUpdate.className='emb-banner-btn';
|
|
4710
4740
|
btnUpdate.textContent=t('update.btn');
|
|
4711
|
-
|
|
4712
|
-
|
|
4741
|
+
var statusDiv=document.createElement('div');
|
|
4742
|
+
statusDiv.style.cssText='font-size:11px;opacity:.8;flex-shrink:0';
|
|
4743
|
+
btnUpdate.onclick=function(){doUpdateInstall(pkgSpec,btnUpdate,statusDiv)};
|
|
4744
|
+
textNode.appendChild(btnUpdate);
|
|
4745
|
+
var spacer=document.createElement('div');
|
|
4746
|
+
spacer.style.cssText='flex:1';
|
|
4713
4747
|
var btnClose=document.createElement('button');
|
|
4748
|
+
btnClose.className='emb-banner-close';
|
|
4714
4749
|
btnClose.innerHTML='×';
|
|
4715
|
-
btnClose.
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
banner.appendChild(
|
|
4750
|
+
btnClose.onclick=function(){banner.remove()};
|
|
4751
|
+
banner.appendChild(textNode);
|
|
4752
|
+
banner.appendChild(statusDiv);
|
|
4753
|
+
banner.appendChild(spacer);
|
|
4719
4754
|
banner.appendChild(btnClose);
|
|
4720
|
-
document.
|
|
4721
|
-
|
|
4755
|
+
var embBanner=document.querySelector('.emb-banner');
|
|
4756
|
+
if(embBanner&&embBanner.parentNode){embBanner.parentNode.insertBefore(banner,embBanner);}
|
|
4757
|
+
else{var ct=document.querySelector('.content-area')||document.querySelector('main')||document.body;if(ct.firstChild)ct.insertBefore(banner,ct.firstChild);else ct.appendChild(banner);}
|
|
4722
4758
|
}catch(e){}
|
|
4723
4759
|
}
|
|
4724
4760
|
|
package/src/viewer/server.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import crypto from "node:crypto";
|
|
3
4
|
import { execSync, exec } from "node:child_process";
|
|
4
5
|
import fs from "node:fs";
|
|
@@ -1177,34 +1178,119 @@ export class ViewerServer {
|
|
|
1177
1178
|
req.on("data", (chunk: Buffer) => { body += chunk.toString(); });
|
|
1178
1179
|
req.on("end", () => {
|
|
1179
1180
|
try {
|
|
1180
|
-
const { packageSpec } = JSON.parse(body);
|
|
1181
|
-
if (!
|
|
1181
|
+
const { packageSpec: rawSpec } = JSON.parse(body);
|
|
1182
|
+
if (!rawSpec || typeof rawSpec !== "string") {
|
|
1182
1183
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1183
1184
|
res.end(JSON.stringify({ ok: false, error: "Missing packageSpec" }));
|
|
1184
1185
|
return;
|
|
1185
1186
|
}
|
|
1187
|
+
const packageSpec = rawSpec.trim().replace(/^(?:npx\s+)?openclaw\s+plugins\s+install\s+/i, "");
|
|
1186
1188
|
const allowed = /^@[\w-]+\/[\w.-]+(@[\w.-]+)?$/;
|
|
1189
|
+
this.log.info(`update-install: received packageSpec="${packageSpec}" (len=${packageSpec.length})`);
|
|
1187
1190
|
if (!allowed.test(packageSpec)) {
|
|
1191
|
+
this.log.warn(`update-install: rejected packageSpec="${packageSpec}" — does not match ${allowed}`);
|
|
1188
1192
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1189
|
-
res.end(JSON.stringify({ ok: false, error:
|
|
1193
|
+
res.end(JSON.stringify({ ok: false, error: `Invalid package spec: "${packageSpec}"` }));
|
|
1190
1194
|
return;
|
|
1191
1195
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1196
|
+
|
|
1197
|
+
const pkgPath = this.findPluginPackageJson();
|
|
1198
|
+
const pluginName = pkgPath
|
|
1199
|
+
? (() => { try { return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).name; } catch { return null; } })()
|
|
1200
|
+
: null;
|
|
1201
|
+
const shortName = pluginName?.replace(/^@[\w-]+\//, "") ?? "memos-local-openclaw-plugin";
|
|
1202
|
+
const extDir = path.join(os.homedir(), ".openclaw", "extensions", shortName);
|
|
1203
|
+
const tmpDir = path.join(os.tmpdir(), `openclaw-update-${Date.now()}`);
|
|
1204
|
+
|
|
1205
|
+
// Download via npm pack, extract, and replace extension dir.
|
|
1206
|
+
// Does NOT touch openclaw.json → no config watcher SIGUSR1.
|
|
1207
|
+
this.log.info(`update-install: downloading ${packageSpec} via npm pack...`);
|
|
1208
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
1209
|
+
exec(`npm pack ${packageSpec} --pack-destination ${tmpDir}`, { timeout: 60_000 }, (packErr, packOut) => {
|
|
1210
|
+
if (packErr) {
|
|
1211
|
+
this.log.warn(`update-install: npm pack failed: ${packErr.message}`);
|
|
1212
|
+
this.jsonResponse(res, { ok: false, error: `Download failed: ${packErr.message}` });
|
|
1213
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
1197
1214
|
return;
|
|
1198
1215
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
this.log.info(`update-install:
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1216
|
+
const tgzFile = packOut.trim().split("\n").pop()!;
|
|
1217
|
+
const tgzPath = path.join(tmpDir, tgzFile);
|
|
1218
|
+
this.log.info(`update-install: downloaded ${tgzFile}, extracting...`);
|
|
1219
|
+
|
|
1220
|
+
const extractDir = path.join(tmpDir, "extract");
|
|
1221
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
1222
|
+
exec(`tar -xzf ${tgzPath} -C ${extractDir}`, { timeout: 30_000 }, (tarErr) => {
|
|
1223
|
+
if (tarErr) {
|
|
1224
|
+
this.log.warn(`update-install: tar extract failed: ${tarErr.message}`);
|
|
1225
|
+
this.jsonResponse(res, { ok: false, error: `Extract failed: ${tarErr.message}` });
|
|
1226
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// npm pack extracts to a "package" subdirectory
|
|
1231
|
+
const srcDir = path.join(extractDir, "package");
|
|
1232
|
+
if (!fs.existsSync(srcDir)) {
|
|
1233
|
+
this.jsonResponse(res, { ok: false, error: "Extracted package has no 'package' dir" });
|
|
1234
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// Replace extension directory
|
|
1239
|
+
this.log.info(`update-install: replacing ${extDir}...`);
|
|
1240
|
+
try { fs.rmSync(extDir, { recursive: true, force: true }); } catch {}
|
|
1241
|
+
fs.mkdirSync(path.dirname(extDir), { recursive: true });
|
|
1242
|
+
fs.renameSync(srcDir, extDir);
|
|
1243
|
+
|
|
1244
|
+
// Install dependencies
|
|
1245
|
+
this.log.info(`update-install: installing dependencies...`);
|
|
1246
|
+
exec(`cd ${extDir} && npm install --omit=dev --ignore-scripts`, { timeout: 120_000 }, (npmErr, npmOut, npmStderr) => {
|
|
1247
|
+
if (npmErr) {
|
|
1248
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
1249
|
+
this.log.warn(`update-install: npm install failed: ${npmErr.message}`);
|
|
1250
|
+
this.jsonResponse(res, { ok: false, error: `Dependency install failed: ${npmStderr || npmErr.message}` });
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Rebuild native modules (do not swallow errors)
|
|
1255
|
+
exec(`cd ${extDir} && npm rebuild better-sqlite3`, { timeout: 60_000 }, (rebuildErr, rebuildOut, rebuildStderr) => {
|
|
1256
|
+
if (rebuildErr) {
|
|
1257
|
+
this.log.warn(`update-install: better-sqlite3 rebuild failed: ${rebuildErr.message}`);
|
|
1258
|
+
const stderr = String(rebuildStderr || "").trim();
|
|
1259
|
+
if (stderr) this.log.warn(`update-install: rebuild stderr: ${stderr.slice(0, 500)}`);
|
|
1260
|
+
// Continue so postinstall.cjs can run (it will try rebuild again and show user guidance)
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// Run postinstall.cjs: legacy cleanup, skill install, version marker, and optional sqlite re-check
|
|
1264
|
+
this.log.info(`update-install: running postinstall...`);
|
|
1265
|
+
exec(`cd ${extDir} && node scripts/postinstall.cjs`, { timeout: 180_000 }, (postErr, postOut, postStderr) => {
|
|
1266
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
1267
|
+
|
|
1268
|
+
if (postErr) {
|
|
1269
|
+
this.log.warn(`update-install: postinstall failed: ${postErr.message}`);
|
|
1270
|
+
const postStderrStr = String(postStderr || "").trim();
|
|
1271
|
+
if (postStderrStr) this.log.warn(`update-install: postinstall stderr: ${postStderrStr.slice(0, 500)}`);
|
|
1272
|
+
// Still report success; plugin is updated, user can run postinstall manually if needed
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// Read new version
|
|
1276
|
+
let newVersion = "unknown";
|
|
1277
|
+
try {
|
|
1278
|
+
const newPkg = JSON.parse(fs.readFileSync(path.join(extDir, "package.json"), "utf-8"));
|
|
1279
|
+
newVersion = newPkg.version ?? newVersion;
|
|
1280
|
+
} catch {}
|
|
1281
|
+
|
|
1282
|
+
this.log.info(`update-install: success! Updated to ${newVersion}`);
|
|
1283
|
+
this.jsonResponse(res, { ok: true, version: newVersion });
|
|
1284
|
+
|
|
1285
|
+
// Trigger Gateway restart after response is sent
|
|
1286
|
+
setTimeout(() => {
|
|
1287
|
+
this.log.info(`update-install: triggering gateway restart...`);
|
|
1288
|
+
process.kill(process.pid, "SIGUSR1");
|
|
1289
|
+
}, 500);
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
1206
1292
|
});
|
|
1207
|
-
}
|
|
1293
|
+
});
|
|
1208
1294
|
});
|
|
1209
1295
|
} catch (e) {
|
|
1210
1296
|
res.writeHead(400, { "Content-Type": "application/json" });
|