@memtensor/memos-local-openclaw-plugin 1.0.6-beta.10 → 1.0.6-beta.12
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/storage/sqlite.d.ts +2 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +15 -0
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +59 -3
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +2 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +66 -0
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +5 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/storage/sqlite.ts +15 -0
- package/src/viewer/html.ts +59 -3
- package/src/viewer/server.ts +54 -0
package/index.ts
CHANGED
|
@@ -1115,6 +1115,10 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1115
1115
|
};
|
|
1116
1116
|
}
|
|
1117
1117
|
|
|
1118
|
+
const disabledWarning = skill.status === "archived"
|
|
1119
|
+
? "\n\n> **Warning:** This skill is currently **disabled** (archived). Its content is shown for reference only — it will not be used in search or auto-recall.\n\n"
|
|
1120
|
+
: "";
|
|
1121
|
+
|
|
1118
1122
|
const manifest = skillInstaller.getCompanionManifest(resolvedSkillId);
|
|
1119
1123
|
let footer = "\n\n---\n";
|
|
1120
1124
|
|
|
@@ -1139,7 +1143,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1139
1143
|
return {
|
|
1140
1144
|
content: [{
|
|
1141
1145
|
type: "text",
|
|
1142
|
-
text: `## Skill: ${skill.name} (v${skill.version})\n\n${sv.content}${footer}`,
|
|
1146
|
+
text: `## Skill: ${skill.name} (v${skill.version})${disabledWarning}\n\n${sv.content}${footer}`,
|
|
1143
1147
|
}],
|
|
1144
1148
|
details: {
|
|
1145
1149
|
skillId: skill.id,
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "MemOS Local Memory",
|
|
4
4
|
"description": "Full-write local conversation memory with hybrid search (RRF + MMR + recency), task summarization, skill evolution, and team sharing (Hub-Client). Provides memory_search, memory_get, task_summary, skill_search, task_share, network_skill_pull, network_team_info, memory_viewer for layered retrieval and team collaboration.",
|
|
5
5
|
"kind": "memory",
|
|
6
|
-
"version": "1.0.6-beta.
|
|
6
|
+
"version": "1.0.6-beta.12",
|
|
7
7
|
"skills": [
|
|
8
8
|
"skill/memos-memory-guide"
|
|
9
9
|
],
|
package/package.json
CHANGED
package/src/storage/sqlite.ts
CHANGED
|
@@ -1469,6 +1469,21 @@ export class SqliteStore {
|
|
|
1469
1469
|
return result.changes > 0;
|
|
1470
1470
|
}
|
|
1471
1471
|
|
|
1472
|
+
disableSkill(skillId: string): boolean {
|
|
1473
|
+
const skill = this.getSkill(skillId);
|
|
1474
|
+
if (!skill || skill.status === "archived") return false;
|
|
1475
|
+
this.db.prepare("DELETE FROM skill_embeddings WHERE skill_id = ?").run(skillId);
|
|
1476
|
+
this.updateSkill(skillId, { status: "archived", installed: 0 });
|
|
1477
|
+
return true;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
enableSkill(skillId: string): boolean {
|
|
1481
|
+
const skill = this.getSkill(skillId);
|
|
1482
|
+
if (!skill || skill.status !== "archived") return false;
|
|
1483
|
+
this.updateSkill(skillId, { status: "active" });
|
|
1484
|
+
return true;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1472
1487
|
// ─── Task CRUD ───
|
|
1473
1488
|
|
|
1474
1489
|
insertTask(task: Task): void {
|
package/src/viewer/html.ts
CHANGED
|
@@ -392,6 +392,13 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
392
392
|
.card-content pre{white-space:pre-wrap;word-break:break-all;background:rgba(0,0,0,.25);padding:14px;border-radius:10px;font-size:12px;font-family:ui-monospace,monospace;margin-top:10px;border:1px solid var(--border);color:var(--text-sec)}
|
|
393
393
|
.card-actions{display:flex;align-items:center;gap:8px;margin-top:14px}
|
|
394
394
|
.card-actions-inline{display:inline-flex;align-items:center;gap:4px;margin-left:auto;flex-shrink:0}
|
|
395
|
+
.btn-warn{color:#f59e0b !important}
|
|
396
|
+
.btn-warn:hover{background:rgba(245,158,11,.15) !important}
|
|
397
|
+
.btn-danger{color:#ef4444 !important}
|
|
398
|
+
.btn-danger:hover{background:rgba(239,68,68,.15) !important}
|
|
399
|
+
.btn-success{color:#10b981 !important}
|
|
400
|
+
.btn-success:hover{background:rgba(16,185,129,.15) !important}
|
|
401
|
+
.skill-card.archived{opacity:0.55;border-style:dashed}
|
|
395
402
|
.vscore-badge{display:inline-flex;align-items:center;background:rgba(59,130,246,.15);color:#60a5fa;font-size:10px;font-weight:700;padding:4px 10px;border-radius:8px;margin-left:auto}
|
|
396
403
|
.merge-badge{display:inline-flex;align-items:center;gap:4px;background:rgba(16,185,129,.12);color:#10b981;font-size:10px;font-weight:600;padding:3px 10px;border-radius:8px}
|
|
397
404
|
.merge-history{margin-top:12px;padding:12px 14px;background:rgba(0,0,0,.15);border-radius:10px;border:1px solid var(--border);font-size:12px;line-height:1.7;color:var(--text-sec);max-height:200px;overflow-y:auto}
|
|
@@ -2385,7 +2392,13 @@ const I18N={
|
|
|
2385
2392
|
'skills.nochangelog':'No changelog',
|
|
2386
2393
|
'skills.status.active':'Active',
|
|
2387
2394
|
'skills.status.draft':'Draft',
|
|
2388
|
-
'skills.status.archived':'
|
|
2395
|
+
'skills.status.archived':'Disabled',
|
|
2396
|
+
'skills.action.disable':'Disable',
|
|
2397
|
+
'skills.action.enable':'Enable',
|
|
2398
|
+
'skills.action.delete':'Delete',
|
|
2399
|
+
'skills.disable.confirm':'Are you sure you want to disable this skill? It will no longer be used in search or auto-recall, but can be re-enabled later.',
|
|
2400
|
+
'skills.disable.error':'Failed to disable skill: ',
|
|
2401
|
+
'skills.enable.error':'Failed to enable skill: ',
|
|
2389
2402
|
'skills.updated':'Updated: ',
|
|
2390
2403
|
'skills.task.prefix':'Task: ',
|
|
2391
2404
|
'tasks.chunks.label':'chunks',
|
|
@@ -3129,7 +3142,13 @@ const I18N={
|
|
|
3129
3142
|
'skills.nochangelog':'暂无变更记录',
|
|
3130
3143
|
'skills.status.active':'生效中',
|
|
3131
3144
|
'skills.status.draft':'草稿',
|
|
3132
|
-
'skills.status.archived':'
|
|
3145
|
+
'skills.status.archived':'已禁用',
|
|
3146
|
+
'skills.action.disable':'禁用',
|
|
3147
|
+
'skills.action.enable':'启用',
|
|
3148
|
+
'skills.action.delete':'删除',
|
|
3149
|
+
'skills.disable.confirm':'确定要禁用此技能吗?禁用后不再参与检索和自动召回,但可以随时重新启用。',
|
|
3150
|
+
'skills.disable.error':'禁用技能失败:',
|
|
3151
|
+
'skills.enable.error':'启用技能失败:',
|
|
3133
3152
|
'skills.updated':'更新于:',
|
|
3134
3153
|
'skills.task.prefix':'任务:',
|
|
3135
3154
|
'tasks.chunks.label':'条记忆',
|
|
@@ -6252,6 +6271,13 @@ async function loadSkills(silent){
|
|
|
6252
6271
|
(skill.status==='active'
|
|
6253
6272
|
?'<button class="btn btn-sm btn-ghost" onclick="openSkillScopeModalFromList("'+escAttr(skill.id)+'","'+skillScope+'")">\\u270F '+t('share.shareBtn')+'</button>'
|
|
6254
6273
|
:'<button class="btn btn-sm btn-ghost" style="opacity:0.45;cursor:not-allowed" onclick="toast(t(\\x27share.scope.skillNotActive\\x27),\\x27warn\\x27)">\\u270F '+t('share.shareBtn')+'</button>')+
|
|
6274
|
+
(skill.status==='active'
|
|
6275
|
+
?'<button class="btn btn-sm btn-ghost btn-warn" onclick="disableSkill("'+escAttr(skill.id)+'")">'+t('skills.action.disable')+'</button>'
|
|
6276
|
+
:'')+
|
|
6277
|
+
(skill.status==='archived'
|
|
6278
|
+
?'<button class="btn btn-sm btn-ghost btn-success" onclick="enableSkill("'+escAttr(skill.id)+'")">'+t('skills.action.enable')+'</button>'
|
|
6279
|
+
:'')+
|
|
6280
|
+
'<button class="btn btn-sm btn-ghost btn-danger" onclick="deleteSkill("'+escAttr(skill.id)+'")">'+t('skills.action.delete')+'</button>'+
|
|
6255
6281
|
'</span>'+
|
|
6256
6282
|
'</div>'+
|
|
6257
6283
|
'</div>';
|
|
@@ -6524,7 +6550,14 @@ async function openSkillDetail(skillId){
|
|
|
6524
6550
|
}
|
|
6525
6551
|
|
|
6526
6552
|
window._currentSkillData=skill;
|
|
6527
|
-
|
|
6553
|
+
var detailActionsHtml='';
|
|
6554
|
+
if(skill.status==='active'){
|
|
6555
|
+
detailActionsHtml+='<button class="btn btn-sm btn-warn" onclick="disableSkill(\''+escAttr(skill.id)+'\')">'+t('skills.action.disable')+'</button>';
|
|
6556
|
+
}else if(skill.status==='archived'){
|
|
6557
|
+
detailActionsHtml+='<button class="btn btn-sm btn-success" onclick="enableSkill(\''+escAttr(skill.id)+'\')">'+t('skills.action.enable')+'</button>';
|
|
6558
|
+
}
|
|
6559
|
+
detailActionsHtml+='<button class="btn btn-sm btn-danger" onclick="deleteSkill(\''+escAttr(skill.id)+'\')">'+t('skills.action.delete')+'</button>';
|
|
6560
|
+
document.getElementById('skillDetailActions').innerHTML=detailActionsHtml;
|
|
6528
6561
|
|
|
6529
6562
|
}catch(e){
|
|
6530
6563
|
document.getElementById('skillDetailTitle').textContent=t('skills.error');
|
|
@@ -7111,6 +7144,29 @@ async function deleteSkill(skillId){
|
|
|
7111
7144
|
loadSkills();
|
|
7112
7145
|
}catch(e){ alert(t('skill.delete.error')+e.message); }
|
|
7113
7146
|
}
|
|
7147
|
+
async function disableSkill(skillId){
|
|
7148
|
+
if(!(await confirmModal(t('skills.disable.confirm')))) return;
|
|
7149
|
+
try{
|
|
7150
|
+
const r=await fetch('/api/skill/'+skillId+'/disable',{method:'PUT'});
|
|
7151
|
+
const d=await r.json();
|
|
7152
|
+
if(!r.ok) throw new Error(d.error||'unknown');
|
|
7153
|
+
toast(t('skills.action.disable')+' ✓','ok');
|
|
7154
|
+
closeSkillDetail();
|
|
7155
|
+
document.getElementById('skillDetailOverlay').classList.remove('show');
|
|
7156
|
+
loadSkills();
|
|
7157
|
+
}catch(e){ alert(t('skills.disable.error')+e.message); }
|
|
7158
|
+
}
|
|
7159
|
+
async function enableSkill(skillId){
|
|
7160
|
+
try{
|
|
7161
|
+
const r=await fetch('/api/skill/'+skillId+'/enable',{method:'PUT'});
|
|
7162
|
+
const d=await r.json();
|
|
7163
|
+
if(!r.ok) throw new Error(d.error||'unknown');
|
|
7164
|
+
toast(t('skills.action.enable')+' ✓','ok');
|
|
7165
|
+
closeSkillDetail();
|
|
7166
|
+
document.getElementById('skillDetailOverlay').classList.remove('show');
|
|
7167
|
+
loadSkills();
|
|
7168
|
+
}catch(e){ alert(t('skills.enable.error')+e.message); }
|
|
7169
|
+
}
|
|
7114
7170
|
|
|
7115
7171
|
|
|
7116
7172
|
function formatDuration(ms){
|
package/src/viewer/server.ts
CHANGED
|
@@ -323,6 +323,8 @@ export class ViewerServer {
|
|
|
323
323
|
else if (p.match(/^\/api\/skill\/[^/]+\/download$/) && req.method === "GET") this.serveSkillDownload(res, p);
|
|
324
324
|
else if (p.match(/^\/api\/skill\/[^/]+\/files$/) && req.method === "GET") this.serveSkillFiles(res, p);
|
|
325
325
|
else if (p.match(/^\/api\/skill\/[^/]+\/visibility$/) && req.method === "PUT") this.handleSkillVisibility(req, res, p);
|
|
326
|
+
else if (p.match(/^\/api\/skill\/[^/]+\/disable$/) && req.method === "PUT") this.handleSkillDisable(res, p);
|
|
327
|
+
else if (p.match(/^\/api\/skill\/[^/]+\/enable$/) && req.method === "PUT") this.handleSkillEnable(res, p);
|
|
326
328
|
else if (p.startsWith("/api/skill/") && req.method === "DELETE") this.handleSkillDelete(res, p);
|
|
327
329
|
else if (p.startsWith("/api/skill/") && req.method === "PUT") this.handleSkillUpdate(req, res, p);
|
|
328
330
|
else if (p.startsWith("/api/skill/") && req.method === "GET") this.serveSkillDetail(res, p);
|
|
@@ -1180,6 +1182,58 @@ export class ViewerServer {
|
|
|
1180
1182
|
});
|
|
1181
1183
|
}
|
|
1182
1184
|
|
|
1185
|
+
private async handleSkillDisable(res: http.ServerResponse, urlPath: string): Promise<void> {
|
|
1186
|
+
const skillId = urlPath.split("/")[3];
|
|
1187
|
+
const skill = this.store.getSkill(skillId);
|
|
1188
|
+
if (!skill) { res.writeHead(404, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Skill not found" })); return; }
|
|
1189
|
+
if (skill.status === "archived") { this.jsonResponse(res, { ok: true, skillId, message: "already disabled" }); return; }
|
|
1190
|
+
|
|
1191
|
+
try {
|
|
1192
|
+
if (skill.visibility === "public") {
|
|
1193
|
+
this.store.setSkillVisibility(skillId, "private");
|
|
1194
|
+
}
|
|
1195
|
+
const hub = this.resolveHubConnection();
|
|
1196
|
+
if (hub) {
|
|
1197
|
+
await hubRequestJson(hub.hubUrl, hub.userToken, "/api/v1/hub/skills/unpublish", {
|
|
1198
|
+
method: "POST",
|
|
1199
|
+
body: JSON.stringify({ sourceSkillId: skillId }),
|
|
1200
|
+
}).catch(() => {});
|
|
1201
|
+
}
|
|
1202
|
+
} catch (_) {}
|
|
1203
|
+
|
|
1204
|
+
try {
|
|
1205
|
+
const workspaceSkillsDir = path.join(this.dataDir, "workspace", "skills");
|
|
1206
|
+
const installedDir = path.join(workspaceSkillsDir, skill.name);
|
|
1207
|
+
if (fs.existsSync(installedDir)) {
|
|
1208
|
+
fs.rmSync(installedDir, { recursive: true, force: true });
|
|
1209
|
+
}
|
|
1210
|
+
} catch (_) {}
|
|
1211
|
+
|
|
1212
|
+
this.store.disableSkill(skillId);
|
|
1213
|
+
this.jsonResponse(res, { ok: true, skillId });
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
private handleSkillEnable(res: http.ServerResponse, urlPath: string): void {
|
|
1217
|
+
const skillId = urlPath.split("/")[3];
|
|
1218
|
+
const skill = this.store.getSkill(skillId);
|
|
1219
|
+
if (!skill) { res.writeHead(404, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Skill not found" })); return; }
|
|
1220
|
+
if (skill.status !== "archived") { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Only disabled (archived) skills can be enabled" })); return; }
|
|
1221
|
+
|
|
1222
|
+
this.store.enableSkill(skillId);
|
|
1223
|
+
|
|
1224
|
+
if (this.embedder) {
|
|
1225
|
+
const sv = this.store.getLatestSkillVersion(skillId);
|
|
1226
|
+
if (sv) {
|
|
1227
|
+
const text = `${skill.name}: ${skill.description}`;
|
|
1228
|
+
this.embedder.embed([text]).then((vecs: number[][]) => {
|
|
1229
|
+
if (vecs.length > 0) this.store.upsertSkillEmbedding(skillId, vecs[0]);
|
|
1230
|
+
}).catch(() => {});
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
this.jsonResponse(res, { ok: true, skillId });
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1183
1237
|
// ─── CRUD ───
|
|
1184
1238
|
|
|
1185
1239
|
private serveMemoryDetail(res: http.ServerResponse, urlPath: string): void {
|