@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/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,
@@ -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.10",
6
+ "version": "1.0.6-beta.12",
7
7
  "skills": [
8
8
  "skill/memos-memory-guide"
9
9
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memtensor/memos-local-openclaw-plugin",
3
- "version": "1.0.6-beta.10",
3
+ "version": "1.0.6-beta.12",
4
4
  "description": "MemOS Local memory plugin for OpenClaw — full-write, hybrid-recall, progressive retrieval",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -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 {
@@ -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':'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(&quot;'+escAttr(skill.id)+'&quot;,&quot;'+skillScope+'&quot;)">\\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(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.disable')+'</button>'
6276
+ :'')+
6277
+ (skill.status==='archived'
6278
+ ?'<button class="btn btn-sm btn-ghost btn-success" onclick="enableSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+t('skills.action.enable')+'</button>'
6279
+ :'')+
6280
+ '<button class="btn btn-sm btn-ghost btn-danger" onclick="deleteSkill(&quot;'+escAttr(skill.id)+'&quot;)">'+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
- document.getElementById('skillDetailActions').innerHTML='';
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){
@@ -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 {