@memtensor/memos-local-openclaw-plugin 1.0.4-beta.4 → 1.0.4-beta.6
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 +22 -39
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +6 -0
- package/dist/capture/index.js.map +1 -1
- package/dist/config.d.ts +1 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -72
- package/dist/config.js.map +1 -1
- package/dist/embedding/index.d.ts +2 -4
- package/dist/embedding/index.d.ts.map +1 -1
- package/dist/embedding/index.js +1 -17
- package/dist/embedding/index.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/ingest/providers/index.d.ts +2 -10
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +43 -209
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/ingest/providers/openai.d.ts +0 -1
- package/dist/ingest/providers/openai.d.ts.map +1 -1
- package/dist/ingest/providers/openai.js +0 -1
- package/dist/ingest/providers/openai.js.map +1 -1
- package/dist/ingest/task-processor.js +1 -1
- package/dist/ingest/task-processor.js.map +1 -1
- package/dist/recall/engine.js +1 -1
- package/dist/recall/engine.js.map +1 -1
- package/dist/shared/llm-call.d.ts +2 -4
- package/dist/shared/llm-call.d.ts.map +1 -1
- package/dist/shared/llm-call.js +81 -20
- package/dist/shared/llm-call.js.map +1 -1
- package/dist/skill/evaluator.d.ts.map +1 -1
- package/dist/skill/evaluator.js +2 -2
- package/dist/skill/evaluator.js.map +1 -1
- package/dist/skill/evolver.d.ts +2 -0
- package/dist/skill/evolver.d.ts.map +1 -1
- package/dist/skill/evolver.js +3 -0
- package/dist/skill/evolver.js.map +1 -1
- package/dist/skill/generator.d.ts.map +1 -1
- package/dist/skill/generator.js +4 -4
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/upgrader.js +1 -1
- package/dist/skill/upgrader.js.map +1 -1
- package/dist/skill/validator.js +1 -1
- package/dist/skill/validator.js.map +1 -1
- package/dist/storage/ensure-binding.d.ts.map +1 -1
- package/dist/storage/ensure-binding.js +1 -3
- package/dist/storage/ensure-binding.js.map +1 -1
- package/dist/storage/sqlite.d.ts +0 -294
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +0 -821
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/telemetry.d.ts +12 -5
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +135 -38
- package/dist/telemetry.js.map +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory-search.d.ts +2 -3
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +7 -48
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/types.d.ts +2 -49
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +471 -2974
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +0 -45
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +18 -1155
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +42 -430
- package/openclaw.plugin.json +1 -2
- package/package.json +3 -4
- package/scripts/postinstall.cjs +46 -283
- package/skill/memos-memory-guide/SKILL.md +2 -26
- package/src/capture/index.ts +8 -0
- package/src/config.ts +3 -94
- package/src/embedding/index.ts +1 -21
- package/src/index.ts +4 -7
- package/src/ingest/providers/index.ts +46 -246
- package/src/ingest/providers/openai.ts +1 -1
- package/src/ingest/task-processor.ts +1 -1
- package/src/recall/engine.ts +1 -1
- package/src/shared/llm-call.ts +95 -23
- package/src/skill/evaluator.ts +2 -3
- package/src/skill/evolver.ts +5 -0
- package/src/skill/generator.ts +4 -6
- package/src/skill/upgrader.ts +1 -1
- package/src/skill/validator.ts +1 -1
- package/src/storage/ensure-binding.ts +1 -3
- package/src/storage/sqlite.ts +0 -1085
- package/src/telemetry.ts +152 -39
- package/src/tools/index.ts +0 -1
- package/src/tools/memory-search.ts +8 -57
- package/src/types.ts +2 -44
- package/src/viewer/html.ts +471 -2974
- package/src/viewer/server.ts +21 -1070
- package/dist/client/connector.d.ts +0 -30
- package/dist/client/connector.d.ts.map +0 -1
- package/dist/client/connector.js +0 -219
- package/dist/client/connector.js.map +0 -1
- package/dist/client/hub.d.ts +0 -61
- package/dist/client/hub.d.ts.map +0 -1
- package/dist/client/hub.js +0 -148
- package/dist/client/hub.js.map +0 -1
- package/dist/client/skill-sync.d.ts +0 -29
- package/dist/client/skill-sync.d.ts.map +0 -1
- package/dist/client/skill-sync.js +0 -216
- package/dist/client/skill-sync.js.map +0 -1
- package/dist/hub/auth.d.ts +0 -19
- package/dist/hub/auth.d.ts.map +0 -1
- package/dist/hub/auth.js +0 -70
- package/dist/hub/auth.js.map +0 -1
- package/dist/hub/server.d.ts +0 -41
- package/dist/hub/server.d.ts.map +0 -1
- package/dist/hub/server.js +0 -747
- package/dist/hub/server.js.map +0 -1
- package/dist/hub/user-manager.d.ts +0 -29
- package/dist/hub/user-manager.d.ts.map +0 -1
- package/dist/hub/user-manager.js +0 -125
- package/dist/hub/user-manager.js.map +0 -1
- package/dist/openclaw-api.d.ts +0 -53
- package/dist/openclaw-api.d.ts.map +0 -1
- package/dist/openclaw-api.js +0 -189
- package/dist/openclaw-api.js.map +0 -1
- package/dist/sharing/types.contract.d.ts +0 -2
- package/dist/sharing/types.contract.d.ts.map +0 -1
- package/dist/sharing/types.contract.js +0 -3
- package/dist/sharing/types.contract.js.map +0 -1
- package/dist/sharing/types.d.ts +0 -80
- package/dist/sharing/types.d.ts.map +0 -1
- package/dist/sharing/types.js +0 -3
- package/dist/sharing/types.js.map +0 -1
- package/dist/tools/network-memory-detail.d.ts +0 -4
- package/dist/tools/network-memory-detail.d.ts.map +0 -1
- package/dist/tools/network-memory-detail.js +0 -34
- package/dist/tools/network-memory-detail.js.map +0 -1
- package/src/client/connector.ts +0 -218
- package/src/client/hub.ts +0 -189
- package/src/client/skill-sync.ts +0 -202
- package/src/hub/auth.ts +0 -78
- package/src/hub/server.ts +0 -740
- package/src/hub/user-manager.ts +0 -139
- package/src/openclaw-api.ts +0 -287
- package/src/sharing/types.contract.ts +0 -40
- package/src/sharing/types.ts +0 -102
- package/src/tools/network-memory-detail.ts +0 -34
package/dist/viewer/server.js
CHANGED
|
@@ -51,10 +51,6 @@ const vector_1 = require("../storage/vector");
|
|
|
51
51
|
const task_processor_1 = require("../ingest/task-processor");
|
|
52
52
|
const engine_1 = require("../recall/engine");
|
|
53
53
|
const evolver_1 = require("../skill/evolver");
|
|
54
|
-
const config_1 = require("../config");
|
|
55
|
-
const connector_1 = require("../client/connector");
|
|
56
|
-
const hub_1 = require("../client/hub");
|
|
57
|
-
const skill_sync_1 = require("../client/skill-sync");
|
|
58
54
|
const html_1 = require("./html");
|
|
59
55
|
const uuid_1 = require("uuid");
|
|
60
56
|
function normalizeTimestamp(ts) {
|
|
@@ -229,7 +225,7 @@ class ViewerServer {
|
|
|
229
225
|
if (p === "/api/memories" && req.method === "GET")
|
|
230
226
|
this.serveMemories(res, url);
|
|
231
227
|
else if (p === "/api/stats")
|
|
232
|
-
this.serveStats(res
|
|
228
|
+
this.serveStats(res);
|
|
233
229
|
else if (p === "/api/metrics")
|
|
234
230
|
this.serveMetrics(res, url);
|
|
235
231
|
else if (p === "/api/tool-metrics")
|
|
@@ -274,76 +270,6 @@ class ViewerServer {
|
|
|
274
270
|
this.serveLogs(res, url);
|
|
275
271
|
else if (p === "/api/log-tools" && req.method === "GET")
|
|
276
272
|
this.serveLogTools(res);
|
|
277
|
-
else if (p === "/api/sharing/status" && req.method === "GET")
|
|
278
|
-
this.serveSharingStatus(res);
|
|
279
|
-
else if (p === "/api/sharing/pending-users" && req.method === "GET")
|
|
280
|
-
this.serveSharingPendingUsers(res);
|
|
281
|
-
else if (p === "/api/sharing/approve-user" && req.method === "POST")
|
|
282
|
-
this.handleSharingApproveUser(req, res);
|
|
283
|
-
else if (p === "/api/sharing/reject-user" && req.method === "POST")
|
|
284
|
-
this.handleSharingRejectUser(req, res);
|
|
285
|
-
else if (p === "/api/sharing/retry-join" && req.method === "POST")
|
|
286
|
-
this.handleRetryJoin(req, res);
|
|
287
|
-
else if (p === "/api/sharing/search/memories" && req.method === "POST")
|
|
288
|
-
this.handleSharingMemorySearch(req, res);
|
|
289
|
-
else if (p === "/api/sharing/memories/list" && req.method === "GET")
|
|
290
|
-
this.serveSharingMemoryList(res, url);
|
|
291
|
-
else if (p === "/api/sharing/tasks/list" && req.method === "GET")
|
|
292
|
-
this.serveSharingTaskList(res, url);
|
|
293
|
-
else if (p === "/api/sharing/skills/list" && req.method === "GET")
|
|
294
|
-
this.serveSharingSkillList(res, url);
|
|
295
|
-
else if (p === "/api/sharing/memory-detail" && req.method === "POST")
|
|
296
|
-
this.handleSharingMemoryDetail(req, res);
|
|
297
|
-
else if (p === "/api/sharing/search/skills" && req.method === "GET")
|
|
298
|
-
this.serveSharingSkillSearch(res, url);
|
|
299
|
-
else if (p === "/api/sharing/tasks/share" && req.method === "POST")
|
|
300
|
-
this.handleSharingTaskShare(req, res);
|
|
301
|
-
else if (p === "/api/sharing/tasks/unshare" && req.method === "POST")
|
|
302
|
-
this.handleSharingTaskUnshare(req, res);
|
|
303
|
-
else if (p === "/api/sharing/update-username" && req.method === "POST")
|
|
304
|
-
this.handleUpdateUsername(req, res);
|
|
305
|
-
else if (p === "/api/sharing/test-hub" && req.method === "POST")
|
|
306
|
-
this.handleTestHubConnection(req, res);
|
|
307
|
-
else if (p === "/api/sharing/memories/share" && req.method === "POST")
|
|
308
|
-
this.handleSharingMemoryShare(req, res);
|
|
309
|
-
else if (p === "/api/sharing/memories/unshare" && req.method === "POST")
|
|
310
|
-
this.handleSharingMemoryUnshare(req, res);
|
|
311
|
-
else if (p === "/api/sharing/skills/pull" && req.method === "POST")
|
|
312
|
-
this.handleSharingSkillPull(req, res);
|
|
313
|
-
else if (p === "/api/sharing/skills/share" && req.method === "POST")
|
|
314
|
-
this.handleSharingSkillShare(req, res);
|
|
315
|
-
else if (p === "/api/sharing/skills/unshare" && req.method === "POST")
|
|
316
|
-
this.handleSharingSkillUnshare(req, res);
|
|
317
|
-
else if (p === "/api/sharing/groups" && req.method === "GET")
|
|
318
|
-
this.serveSharingGroups(res);
|
|
319
|
-
else if (p === "/api/sharing/groups" && req.method === "POST")
|
|
320
|
-
this.handleSharingGroupCreate(req, res);
|
|
321
|
-
else if (p.match(/^\/api\/sharing\/groups\/[^/]+$/) && req.method === "PUT")
|
|
322
|
-
this.handleSharingGroupUpdate(req, res, p);
|
|
323
|
-
else if (p.match(/^\/api\/sharing\/groups\/[^/]+$/) && req.method === "DELETE")
|
|
324
|
-
this.handleSharingGroupDelete(res, p);
|
|
325
|
-
else if (p.match(/^\/api\/sharing\/groups\/[^/]+\/members$/) && req.method === "GET")
|
|
326
|
-
this.serveSharingGroupMembers(res, p);
|
|
327
|
-
else if (p.match(/^\/api\/sharing\/groups\/[^/]+\/members$/) && req.method === "POST")
|
|
328
|
-
this.handleSharingGroupAddMember(req, res, p);
|
|
329
|
-
else if (p.match(/^\/api\/sharing\/groups\/[^/]+\/members$/) && req.method === "DELETE")
|
|
330
|
-
this.handleSharingGroupRemoveMember(req, res, p);
|
|
331
|
-
else if (p === "/api/sharing/users" && req.method === "GET")
|
|
332
|
-
this.serveSharingUsers(res);
|
|
333
|
-
else if (p === "/api/admin/shared-tasks" && req.method === "GET")
|
|
334
|
-
this.serveAdminSharedTasks(res);
|
|
335
|
-
else if (p.match(/^\/api\/admin\/shared-tasks\/[^/]+$/) && req.method === "DELETE")
|
|
336
|
-
this.handleAdminDeleteTask(res, p);
|
|
337
|
-
else if (p === "/api/admin/shared-skills" && req.method === "GET")
|
|
338
|
-
this.serveAdminSharedSkills(res);
|
|
339
|
-
else if (p.match(/^\/api\/admin\/shared-skills\/[^/]+$/) && req.method === "DELETE")
|
|
340
|
-
this.handleAdminDeleteSkill(res, p);
|
|
341
|
-
else if (p === "/api/admin/shared-memories" && req.method === "GET")
|
|
342
|
-
this.serveAdminSharedMemories(res);
|
|
343
|
-
else if (p.match(/^\/api\/admin\/shared-memories\/[^/]+$/) && req.method === "DELETE")
|
|
344
|
-
this.handleAdminDeleteMemory(res, p);
|
|
345
|
-
else if (p === "/api/local-ips" && req.method === "GET")
|
|
346
|
-
this.serveLocalIPs(res);
|
|
347
273
|
else if (p === "/api/config" && req.method === "GET")
|
|
348
274
|
this.serveConfig(res);
|
|
349
275
|
else if (p === "/api/config" && req.method === "PUT")
|
|
@@ -529,28 +455,15 @@ class ViewerServer {
|
|
|
529
455
|
const totalRow = db.prepare("SELECT COUNT(*) as count FROM chunks" + where).get(...params);
|
|
530
456
|
const rawMemories = db.prepare("SELECT * FROM chunks" + where + ` ORDER BY created_at ${sortBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
531
457
|
const findMergeSources = db.prepare("SELECT id, summary, role FROM chunks WHERE dedup_target = ? AND (dedup_status = 'merged' OR dedup_status = 'duplicate')");
|
|
532
|
-
const chunkIds = rawMemories.map((m) => m.id);
|
|
533
|
-
const sharingMap = new Map();
|
|
534
|
-
if (chunkIds.length > 0) {
|
|
535
|
-
try {
|
|
536
|
-
const placeholders = chunkIds.map(() => "?").join(",");
|
|
537
|
-
const sharedRows = db.prepare(`SELECT source_chunk_id, visibility, group_id FROM hub_memories WHERE source_chunk_id IN (${placeholders})`).all(...chunkIds);
|
|
538
|
-
for (const r of sharedRows)
|
|
539
|
-
sharingMap.set(r.source_chunk_id, r);
|
|
540
|
-
}
|
|
541
|
-
catch {
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
458
|
const memories = rawMemories.map((m) => {
|
|
545
|
-
|
|
546
|
-
|
|
459
|
+
if (m.role === "user" && m.content) {
|
|
460
|
+
m = { ...m, content: (0, capture_1.stripInboundMetadata)(m.content) };
|
|
461
|
+
}
|
|
462
|
+
if (m.merge_count > 0) {
|
|
547
463
|
const sources = findMergeSources.all(m.id);
|
|
548
|
-
|
|
464
|
+
m.merge_sources = sources;
|
|
549
465
|
}
|
|
550
|
-
|
|
551
|
-
out.sharingVisibility = shared?.visibility ?? null;
|
|
552
|
-
out.sharingGroupId = shared?.group_id ?? null;
|
|
553
|
-
return out;
|
|
466
|
+
return m;
|
|
554
467
|
});
|
|
555
468
|
this.store.recordViewerEvent("list");
|
|
556
469
|
this.jsonResponse(res, {
|
|
@@ -615,7 +528,6 @@ class ViewerServer {
|
|
|
615
528
|
}));
|
|
616
529
|
const db = this.store.db;
|
|
617
530
|
const meta = db.prepare("SELECT skill_status, skill_reason FROM tasks WHERE id = ?").get(taskId);
|
|
618
|
-
const sharedTask = db.prepare("SELECT visibility, group_id FROM hub_tasks WHERE source_task_id = ? ORDER BY updated_at DESC LIMIT 1").get(taskId);
|
|
619
531
|
this.jsonResponse(res, {
|
|
620
532
|
id: task.id,
|
|
621
533
|
sessionKey: task.sessionKey,
|
|
@@ -628,11 +540,9 @@ class ViewerServer {
|
|
|
628
540
|
skillStatus: meta?.skill_status ?? null,
|
|
629
541
|
skillReason: meta?.skill_reason ?? null,
|
|
630
542
|
skillLinks,
|
|
631
|
-
sharingVisibility: sharedTask?.visibility ?? null,
|
|
632
|
-
sharingGroupId: sharedTask?.group_id ?? null,
|
|
633
543
|
});
|
|
634
544
|
}
|
|
635
|
-
serveStats(res
|
|
545
|
+
serveStats(res) {
|
|
636
546
|
const emptyStats = {
|
|
637
547
|
totalMemories: 0, totalSessions: 0, totalEmbeddings: 0, totalSkills: 0,
|
|
638
548
|
embeddingProvider: this.embedder?.provider ?? "none",
|
|
@@ -644,7 +554,6 @@ class ViewerServer {
|
|
|
644
554
|
this.jsonResponse(res, emptyStats);
|
|
645
555
|
return;
|
|
646
556
|
}
|
|
647
|
-
const ownerFilter = url?.searchParams.get("owner") ?? "";
|
|
648
557
|
try {
|
|
649
558
|
const db = this.store.db;
|
|
650
559
|
const total = db.prepare("SELECT COUNT(*) as count FROM chunks").get();
|
|
@@ -663,12 +572,7 @@ class ViewerServer {
|
|
|
663
572
|
embCount = db.prepare("SELECT COUNT(*) as count FROM embeddings").get().count;
|
|
664
573
|
}
|
|
665
574
|
catch { /* table may not exist */ }
|
|
666
|
-
const
|
|
667
|
-
? "SELECT session_key, COUNT(*) as count, MIN(created_at) as earliest, MAX(created_at) as latest FROM chunks WHERE owner = ? GROUP BY session_key ORDER BY latest DESC"
|
|
668
|
-
: "SELECT session_key, COUNT(*) as count, MIN(created_at) as earliest, MAX(created_at) as latest FROM chunks GROUP BY session_key ORDER BY latest DESC";
|
|
669
|
-
const sessionList = (ownerFilter
|
|
670
|
-
? db.prepare(sessionQuery).all(ownerFilter)
|
|
671
|
-
: db.prepare(sessionQuery).all());
|
|
575
|
+
const sessionList = db.prepare("SELECT session_key, COUNT(*) as count, MIN(created_at) as earliest, MAX(created_at) as latest FROM chunks GROUP BY session_key ORDER BY latest DESC").all();
|
|
672
576
|
let skillCount = 0;
|
|
673
577
|
try {
|
|
674
578
|
skillCount = db.prepare("SELECT COUNT(*) as count FROM skills").get().count;
|
|
@@ -830,10 +734,8 @@ class ViewerServer {
|
|
|
830
734
|
const versions = this.store.getSkillVersions(skillId);
|
|
831
735
|
const relatedTasks = this.store.getTasksBySkill(skillId);
|
|
832
736
|
const files = node_fs_1.default.existsSync(skill.dirPath) ? this.walkDir(skill.dirPath, skill.dirPath) : [];
|
|
833
|
-
const db = this.store.db;
|
|
834
|
-
const sharedSkill = db.prepare("SELECT visibility, group_id FROM hub_skills WHERE source_skill_id = ? ORDER BY updated_at DESC LIMIT 1").get(skillId);
|
|
835
737
|
this.jsonResponse(res, {
|
|
836
|
-
skill
|
|
738
|
+
skill,
|
|
837
739
|
versions: versions.map(v => ({
|
|
838
740
|
id: v.id,
|
|
839
741
|
version: v.version,
|
|
@@ -942,7 +844,7 @@ class ViewerServer {
|
|
|
942
844
|
handleSkillVisibility(req, res, urlPath) {
|
|
943
845
|
const segments = urlPath.split("/");
|
|
944
846
|
const skillId = segments[segments.length - 2];
|
|
945
|
-
this.readBody(req,
|
|
847
|
+
this.readBody(req, (body) => {
|
|
946
848
|
try {
|
|
947
849
|
const parsed = JSON.parse(body);
|
|
948
850
|
const visibility = parsed.visibility;
|
|
@@ -958,47 +860,7 @@ class ViewerServer {
|
|
|
958
860
|
return;
|
|
959
861
|
}
|
|
960
862
|
this.store.setSkillVisibility(skillId, visibility);
|
|
961
|
-
|
|
962
|
-
const sharing = this.ctx?.config?.sharing;
|
|
963
|
-
if (sharing?.enabled && this.ctx) {
|
|
964
|
-
try {
|
|
965
|
-
const hubClient = await this.resolveHubClientAware();
|
|
966
|
-
if (visibility === "public") {
|
|
967
|
-
const bundle = (0, skill_sync_1.buildSkillBundleForHub)(this.store, skillId);
|
|
968
|
-
const response = await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/skills/publish", {
|
|
969
|
-
method: "POST",
|
|
970
|
-
body: JSON.stringify({ visibility: "public", groupId: null, metadata: bundle.metadata, bundle: bundle.bundle }),
|
|
971
|
-
});
|
|
972
|
-
if (hubClient.userId) {
|
|
973
|
-
const existing = this.store.getHubSkillBySource(hubClient.userId, skillId);
|
|
974
|
-
this.store.upsertHubSkill({
|
|
975
|
-
id: response?.skillId ?? existing?.id ?? node_crypto_1.default.randomUUID(),
|
|
976
|
-
sourceSkillId: skillId, sourceUserId: hubClient.userId,
|
|
977
|
-
name: skill.name, description: skill.description, version: skill.version,
|
|
978
|
-
groupId: null, visibility: "public",
|
|
979
|
-
bundle: JSON.stringify(bundle.bundle), qualityScore: skill.qualityScore,
|
|
980
|
-
createdAt: existing?.createdAt ?? Date.now(), updatedAt: Date.now(),
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
hubSynced = true;
|
|
984
|
-
this.log.info(`Skill "${skill.name}" published to Hub`);
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/skills/unpublish", {
|
|
988
|
-
method: "POST",
|
|
989
|
-
body: JSON.stringify({ sourceSkillId: skillId }),
|
|
990
|
-
});
|
|
991
|
-
if (hubClient.userId)
|
|
992
|
-
this.store.deleteHubSkillBySource(hubClient.userId, skillId);
|
|
993
|
-
hubSynced = true;
|
|
994
|
-
this.log.info(`Skill "${skill.name}" unpublished from Hub`);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
catch (hubErr) {
|
|
998
|
-
this.log.warn(`Hub sync failed for skill visibility change: ${hubErr}`);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
this.jsonResponse(res, { ok: true, skillId, visibility, hubSynced });
|
|
863
|
+
this.jsonResponse(res, { ok: true, skillId, visibility });
|
|
1002
864
|
}
|
|
1003
865
|
catch (err) {
|
|
1004
866
|
const errMsg = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
@@ -1207,880 +1069,8 @@ class ViewerServer {
|
|
|
1207
1069
|
// ─── Config API ───
|
|
1208
1070
|
getOpenClawConfigPath() {
|
|
1209
1071
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
getPluginEntryConfig(raw) {
|
|
1213
|
-
const entries = raw?.plugins?.entries ?? {};
|
|
1214
|
-
return entries["memos-local-openclaw-plugin"]?.config
|
|
1215
|
-
?? entries["memos-lite-openclaw-plugin"]?.config
|
|
1216
|
-
?? entries["memos-lite"]?.config
|
|
1217
|
-
?? {};
|
|
1218
|
-
}
|
|
1219
|
-
getResolvedViewerConfig(raw) {
|
|
1220
|
-
const pluginCfg = this.getPluginEntryConfig(raw);
|
|
1221
|
-
const stateDir = this.ctx?.stateDir ?? this.getOpenClawHome();
|
|
1222
|
-
return (0, config_1.resolveConfig)(pluginCfg, stateDir);
|
|
1223
|
-
}
|
|
1224
|
-
hasUsableEmbeddingProvider(cfg) {
|
|
1225
|
-
const embedding = cfg.embedding;
|
|
1226
|
-
if (!embedding?.provider)
|
|
1227
|
-
return false;
|
|
1228
|
-
if (embedding.provider === "openclaw") {
|
|
1229
|
-
return !!(this.ctx?.openclawAPI) && embedding.capabilities?.hostEmbedding === true;
|
|
1230
|
-
}
|
|
1231
|
-
return true;
|
|
1232
|
-
}
|
|
1233
|
-
hasUsableSummarizerProvider(cfg) {
|
|
1234
|
-
const summarizer = cfg.summarizer;
|
|
1235
|
-
if (!summarizer?.provider)
|
|
1236
|
-
return false;
|
|
1237
|
-
if (summarizer.provider === "openclaw") {
|
|
1238
|
-
return !!(this.ctx?.openclawAPI) && summarizer.capabilities?.hostCompletion === true;
|
|
1239
|
-
}
|
|
1240
|
-
return true;
|
|
1241
|
-
}
|
|
1242
|
-
async serveSharingStatus(res) {
|
|
1243
|
-
const sharing = this.ctx?.config?.sharing;
|
|
1244
|
-
const persisted = this.store.getClientHubConnection();
|
|
1245
|
-
const resolvedHubUrl = sharing?.client?.hubAddress ? (0, hub_1.normalizeHubUrl)(sharing.client.hubAddress) : persisted?.hubUrl ?? null;
|
|
1246
|
-
const hasClientConfig = Boolean((sharing?.client?.hubAddress && sharing?.client?.userToken) ||
|
|
1247
|
-
(persisted?.hubUrl && persisted?.userToken));
|
|
1248
|
-
const base = {
|
|
1249
|
-
enabled: Boolean(sharing?.enabled),
|
|
1250
|
-
role: sharing?.role ?? null,
|
|
1251
|
-
clientConfigured: hasClientConfig,
|
|
1252
|
-
hubUrl: resolvedHubUrl,
|
|
1253
|
-
connection: { connected: false, user: null, hubUrl: undefined, teamName: null, apiVersion: null },
|
|
1254
|
-
admin: { canManageUsers: false, rejectSupported: false },
|
|
1255
|
-
};
|
|
1256
|
-
if (!this.ctx || !sharing?.enabled) {
|
|
1257
|
-
this.jsonResponse(res, base);
|
|
1258
|
-
return;
|
|
1259
|
-
}
|
|
1260
|
-
// Hub 模式下,本机就是管理者,直接赋予 admin 权限
|
|
1261
|
-
if (sharing.role === "hub") {
|
|
1262
|
-
base.admin.canManageUsers = true;
|
|
1263
|
-
base.admin.rejectSupported = true;
|
|
1264
|
-
base.connection.connected = true;
|
|
1265
|
-
base.connection.hubUrl = resolvedHubUrl ?? undefined;
|
|
1266
|
-
// 通过 hub API 获取 admin 用户的真实信息(含分组)
|
|
1267
|
-
let adminUser = { username: "hub-admin", role: "admin", groups: [] };
|
|
1268
|
-
try {
|
|
1269
|
-
const hub = this.resolveHubConnection();
|
|
1270
|
-
if (hub) {
|
|
1271
|
-
const me = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/me", { method: "GET" });
|
|
1272
|
-
if (me) {
|
|
1273
|
-
adminUser = {
|
|
1274
|
-
id: me.id,
|
|
1275
|
-
username: me.username ?? "hub-admin",
|
|
1276
|
-
role: me.role ?? "admin",
|
|
1277
|
-
groups: Array.isArray(me.groups) ? me.groups : [],
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
catch { /* fallback to default */ }
|
|
1283
|
-
base.connection.user = adminUser;
|
|
1284
|
-
// Fetch team info from own hub
|
|
1285
|
-
try {
|
|
1286
|
-
const selfUrl = resolvedHubUrl || `http://localhost:${sharing.hub?.port ?? 21816}`;
|
|
1287
|
-
const info = await fetch(`${selfUrl}/api/v1/hub/info`).then(r => r.ok ? r.json() : null).catch(() => null);
|
|
1288
|
-
base.connection.teamName = info?.teamName ?? sharing.hub?.teamName ?? null;
|
|
1289
|
-
base.connection.apiVersion = info?.apiVersion ?? null;
|
|
1290
|
-
}
|
|
1291
|
-
catch { /* ignore */ }
|
|
1292
|
-
this.jsonResponse(res, base);
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
const hasPendingConnection = Boolean(persisted?.hubUrl && persisted?.userId && !persisted?.userToken);
|
|
1296
|
-
if (!hasClientConfig && !hasPendingConnection) {
|
|
1297
|
-
this.jsonResponse(res, base);
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
try {
|
|
1301
|
-
const status = await (0, connector_1.getHubStatus)(this.store, this.ctx.config);
|
|
1302
|
-
const output = { ...base, connection: { ...base.connection, ...status } };
|
|
1303
|
-
if (status.user?.status === "pending") {
|
|
1304
|
-
output.connection.pendingApproval = true;
|
|
1305
|
-
}
|
|
1306
|
-
if (status.user?.status === "rejected") {
|
|
1307
|
-
output.connection.rejected = true;
|
|
1308
|
-
}
|
|
1309
|
-
if (status.connected && status.hubUrl) {
|
|
1310
|
-
try {
|
|
1311
|
-
const info = await fetch(`${status.hubUrl}/api/v1/hub/info`).then((r) => (r.ok ? r.json() : null)).catch(() => null);
|
|
1312
|
-
output.connection.teamName = info?.teamName ?? null;
|
|
1313
|
-
output.connection.apiVersion = info?.apiVersion ?? null;
|
|
1314
|
-
}
|
|
1315
|
-
catch { }
|
|
1316
|
-
}
|
|
1317
|
-
else if (status.hubUrl) {
|
|
1318
|
-
try {
|
|
1319
|
-
const info = await fetch(`${status.hubUrl}/api/v1/hub/info`).then((r) => (r.ok ? r.json() : null)).catch(() => null);
|
|
1320
|
-
output.connection.teamName = info?.teamName ?? null;
|
|
1321
|
-
}
|
|
1322
|
-
catch { }
|
|
1323
|
-
}
|
|
1324
|
-
output.admin.canManageUsers = status.connected && status.user?.role === "admin";
|
|
1325
|
-
output.admin.rejectSupported = output.admin.canManageUsers;
|
|
1326
|
-
this.jsonResponse(res, output);
|
|
1327
|
-
}
|
|
1328
|
-
catch (err) {
|
|
1329
|
-
this.jsonResponse(res, { ...base, error: String(err) });
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
async serveSharingPendingUsers(res) {
|
|
1333
|
-
if (!this.ctx)
|
|
1334
|
-
return this.jsonResponse(res, { users: [], error: "sharing_unavailable" });
|
|
1335
|
-
try {
|
|
1336
|
-
const hub = this.resolveHubConnection();
|
|
1337
|
-
if (!hub)
|
|
1338
|
-
return this.jsonResponse(res, { users: [], error: "not_configured" });
|
|
1339
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/pending-users", { method: "GET" });
|
|
1340
|
-
this.jsonResponse(res, { users: Array.isArray(data?.users) ? data.users : [] });
|
|
1341
|
-
}
|
|
1342
|
-
catch (err) {
|
|
1343
|
-
this.jsonResponse(res, { users: [], error: String(err) });
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
handleSharingApproveUser(req, res) {
|
|
1347
|
-
this.readBody(req, async (body) => {
|
|
1348
|
-
if (!this.ctx)
|
|
1349
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1350
|
-
try {
|
|
1351
|
-
const parsed = JSON.parse(body || "{}");
|
|
1352
|
-
const hub = this.resolveHubConnection();
|
|
1353
|
-
if (!hub)
|
|
1354
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1355
|
-
const result = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/approve-user", {
|
|
1356
|
-
method: "POST",
|
|
1357
|
-
body: JSON.stringify({ userId: parsed.userId, username: parsed.username }),
|
|
1358
|
-
});
|
|
1359
|
-
this.jsonResponse(res, { ok: true, result });
|
|
1360
|
-
}
|
|
1361
|
-
catch (err) {
|
|
1362
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1363
|
-
}
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
handleSharingRejectUser(req, res) {
|
|
1367
|
-
this.readBody(req, async (body) => {
|
|
1368
|
-
if (!this.ctx)
|
|
1369
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1370
|
-
try {
|
|
1371
|
-
const parsed = JSON.parse(body || "{}");
|
|
1372
|
-
const hub = this.resolveHubConnection();
|
|
1373
|
-
if (!hub)
|
|
1374
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1375
|
-
const result = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/reject-user", {
|
|
1376
|
-
method: "POST",
|
|
1377
|
-
body: JSON.stringify({ userId: parsed.userId }),
|
|
1378
|
-
});
|
|
1379
|
-
this.jsonResponse(res, { ok: true, result });
|
|
1380
|
-
}
|
|
1381
|
-
catch (err) {
|
|
1382
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1383
|
-
}
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
handleRetryJoin(req, res) {
|
|
1387
|
-
this.readBody(req, async (_body) => {
|
|
1388
|
-
if (!this.ctx)
|
|
1389
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1390
|
-
const sharing = this.ctx.config.sharing;
|
|
1391
|
-
if (!sharing?.enabled || sharing.role !== "client") {
|
|
1392
|
-
return this.jsonResponse(res, { ok: false, error: "not_in_client_mode" });
|
|
1393
|
-
}
|
|
1394
|
-
const hubAddress = sharing.client?.hubAddress ?? "";
|
|
1395
|
-
const teamToken = sharing.client?.teamToken ?? "";
|
|
1396
|
-
if (!hubAddress || !teamToken) {
|
|
1397
|
-
return this.jsonResponse(res, { ok: false, error: "missing_hub_address_or_team_token" });
|
|
1398
|
-
}
|
|
1399
|
-
try {
|
|
1400
|
-
const hubUrl = (0, hub_1.normalizeHubUrl)(hubAddress);
|
|
1401
|
-
const os = await Promise.resolve().then(() => __importStar(require("os")));
|
|
1402
|
-
const username = os.userInfo().username || "user";
|
|
1403
|
-
const hostname = os.hostname() || "unknown";
|
|
1404
|
-
const result = await (0, hub_1.hubRequestJson)(hubUrl, "", "/api/v1/hub/join", {
|
|
1405
|
-
method: "POST",
|
|
1406
|
-
body: JSON.stringify({ teamToken, username, deviceName: hostname }),
|
|
1407
|
-
});
|
|
1408
|
-
this.store.setClientHubConnection({
|
|
1409
|
-
hubUrl,
|
|
1410
|
-
userId: String(result.userId || ""),
|
|
1411
|
-
username,
|
|
1412
|
-
userToken: result.userToken || "",
|
|
1413
|
-
role: "member",
|
|
1414
|
-
connectedAt: Date.now(),
|
|
1415
|
-
});
|
|
1416
|
-
this.jsonResponse(res, { ok: true, status: result.status || "pending" });
|
|
1417
|
-
}
|
|
1418
|
-
catch (err) {
|
|
1419
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1420
|
-
}
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
async serveSharingMemoryList(res, url) {
|
|
1424
|
-
if (!this.ctx)
|
|
1425
|
-
return this.jsonResponse(res, { memories: [], error: "sharing_unavailable" });
|
|
1426
|
-
try {
|
|
1427
|
-
const limit = Number(url.searchParams.get("limit") || 40);
|
|
1428
|
-
const data = await (0, hub_1.hubListMemories)(this.store, this.ctx, { limit });
|
|
1429
|
-
this.jsonResponse(res, { memories: Array.isArray(data?.memories) ? data.memories : [] });
|
|
1430
|
-
}
|
|
1431
|
-
catch (err) {
|
|
1432
|
-
this.jsonResponse(res, { memories: [], error: String(err) });
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
async serveSharingTaskList(res, url) {
|
|
1436
|
-
if (!this.ctx)
|
|
1437
|
-
return this.jsonResponse(res, { tasks: [], error: "sharing_unavailable" });
|
|
1438
|
-
try {
|
|
1439
|
-
const limit = Number(url.searchParams.get("limit") || 40);
|
|
1440
|
-
const data = await (0, hub_1.hubListTasks)(this.store, this.ctx, { limit });
|
|
1441
|
-
this.jsonResponse(res, { tasks: Array.isArray(data?.tasks) ? data.tasks : [] });
|
|
1442
|
-
}
|
|
1443
|
-
catch (err) {
|
|
1444
|
-
this.jsonResponse(res, { tasks: [], error: String(err) });
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
async serveSharingSkillList(res, url) {
|
|
1448
|
-
if (!this.ctx)
|
|
1449
|
-
return this.jsonResponse(res, { skills: [], error: "sharing_unavailable" });
|
|
1450
|
-
try {
|
|
1451
|
-
const limit = Number(url.searchParams.get("limit") || 40);
|
|
1452
|
-
const data = await (0, hub_1.hubListSkills)(this.store, this.ctx, { limit });
|
|
1453
|
-
this.jsonResponse(res, { skills: Array.isArray(data?.skills) ? data.skills : [] });
|
|
1454
|
-
}
|
|
1455
|
-
catch (err) {
|
|
1456
|
-
this.jsonResponse(res, { skills: [], error: String(err) });
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
handleSharingMemorySearch(req, res) {
|
|
1460
|
-
this.readBody(req, async (body) => {
|
|
1461
|
-
if (!this.ctx)
|
|
1462
|
-
return this.jsonResponse(res, { local: { hits: [], meta: {} }, hub: { hits: [], meta: { totalCandidates: 0, searchedGroups: [], includedPublic: false } }, error: "sharing_unavailable" });
|
|
1463
|
-
const emptyHub = { hits: [], meta: { totalCandidates: 0, searchedGroups: [], includedPublic: false } };
|
|
1464
|
-
try {
|
|
1465
|
-
const parsed = JSON.parse(body || "{}");
|
|
1466
|
-
const query = String(parsed.query || "");
|
|
1467
|
-
const role = typeof parsed.role === "string" ? parsed.role : undefined;
|
|
1468
|
-
const maxResults = typeof parsed.maxResults === "number" ? parsed.maxResults : 10;
|
|
1469
|
-
const scope = parsed.scope === "group" || parsed.scope === "all" ? parsed.scope : "local";
|
|
1470
|
-
const local = this.searchLocalViewerMemories(query, { role, maxResults });
|
|
1471
|
-
if (scope === "local") {
|
|
1472
|
-
return this.jsonResponse(res, { local: { hits: local.hits, meta: local.meta }, hub: emptyHub });
|
|
1473
|
-
}
|
|
1474
|
-
try {
|
|
1475
|
-
const hub = await (0, hub_1.hubSearchMemories)(this.store, this.ctx, { query, maxResults, scope });
|
|
1476
|
-
this.jsonResponse(res, { local: { hits: local.hits, meta: local.meta }, hub });
|
|
1477
|
-
}
|
|
1478
|
-
catch (err) {
|
|
1479
|
-
this.jsonResponse(res, { local: { hits: local.hits, meta: local.meta }, hub: emptyHub, error: String(err) });
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
catch (err) {
|
|
1483
|
-
this.jsonResponse(res, { local: { hits: [], meta: {} }, hub: emptyHub, error: String(err) });
|
|
1484
|
-
}
|
|
1485
|
-
});
|
|
1486
|
-
}
|
|
1487
|
-
handleSharingMemoryDetail(req, res) {
|
|
1488
|
-
this.readBody(req, async (body) => {
|
|
1489
|
-
if (!this.ctx)
|
|
1490
|
-
return this.jsonResponse(res, { error: "sharing_unavailable" });
|
|
1491
|
-
try {
|
|
1492
|
-
const parsed = JSON.parse(body || "{}");
|
|
1493
|
-
const detail = await (0, hub_1.hubGetMemoryDetail)(this.store, this.ctx, { remoteHitId: String(parsed.remoteHitId || "") });
|
|
1494
|
-
this.jsonResponse(res, detail);
|
|
1495
|
-
}
|
|
1496
|
-
catch (err) {
|
|
1497
|
-
this.jsonResponse(res, { error: String(err) });
|
|
1498
|
-
}
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1501
|
-
async serveSharingSkillSearch(res, url) {
|
|
1502
|
-
if (!this.ctx)
|
|
1503
|
-
return this.jsonResponse(res, { local: { hits: [] }, hub: { hits: [] }, error: "sharing_unavailable" });
|
|
1504
|
-
try {
|
|
1505
|
-
const query = String(url.searchParams.get("query") || "");
|
|
1506
|
-
const scope = url.searchParams.get("scope") === "group" || url.searchParams.get("scope") === "all" ? url.searchParams.get("scope") : "local";
|
|
1507
|
-
const recall = new engine_1.RecallEngine(this.store, this.embedder, this.ctx);
|
|
1508
|
-
const localHits = await recall.searchSkills(query, "mix", "agent:main");
|
|
1509
|
-
if (scope === "local") {
|
|
1510
|
-
return this.jsonResponse(res, { local: { hits: localHits }, hub: { hits: [] } });
|
|
1511
|
-
}
|
|
1512
|
-
try {
|
|
1513
|
-
const hub = await (0, hub_1.hubSearchSkills)(this.store, this.ctx, { query, maxResults: Number(url.searchParams.get("maxResults") || 20) });
|
|
1514
|
-
this.jsonResponse(res, { local: { hits: localHits }, hub });
|
|
1515
|
-
}
|
|
1516
|
-
catch (err) {
|
|
1517
|
-
this.jsonResponse(res, { local: { hits: localHits }, hub: { hits: [] }, error: String(err) });
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
catch (err) {
|
|
1521
|
-
this.jsonResponse(res, { local: { hits: [] }, hub: { hits: [] }, error: String(err) });
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
searchLocalViewerMemories(query, options) {
|
|
1525
|
-
const db = this.store.db;
|
|
1526
|
-
const role = options?.role;
|
|
1527
|
-
const maxResults = options?.maxResults ?? 10;
|
|
1528
|
-
const params = [];
|
|
1529
|
-
let rows = [];
|
|
1530
|
-
try {
|
|
1531
|
-
let sql = "SELECT c.* FROM chunks_fts f JOIN chunks c ON f.rowid = c.rowid WHERE chunks_fts MATCH ?";
|
|
1532
|
-
params.push(query);
|
|
1533
|
-
if (role) {
|
|
1534
|
-
sql += " AND c.role = ?";
|
|
1535
|
-
params.push(role);
|
|
1536
|
-
}
|
|
1537
|
-
sql += " ORDER BY rank LIMIT ?";
|
|
1538
|
-
params.push(maxResults);
|
|
1539
|
-
rows = db.prepare(sql).all(...params);
|
|
1540
|
-
}
|
|
1541
|
-
catch {
|
|
1542
|
-
const likeParams = [`%${query}%`, `%${query}%`];
|
|
1543
|
-
let sql = "SELECT * FROM chunks WHERE (content LIKE ? OR summary LIKE ?)";
|
|
1544
|
-
if (role) {
|
|
1545
|
-
sql += " AND role = ?";
|
|
1546
|
-
likeParams.push(role);
|
|
1547
|
-
}
|
|
1548
|
-
sql += " ORDER BY created_at DESC LIMIT ?";
|
|
1549
|
-
likeParams.push(maxResults);
|
|
1550
|
-
rows = db.prepare(sql).all(...likeParams);
|
|
1551
|
-
}
|
|
1552
|
-
const hits = rows.map((row, idx) => ({
|
|
1553
|
-
id: row.id,
|
|
1554
|
-
summary: row.summary || row.content?.slice(0, 120) || "",
|
|
1555
|
-
excerpt: row.content || "",
|
|
1556
|
-
score: Math.max(0.3, 1 - idx * 0.1),
|
|
1557
|
-
role: row.role,
|
|
1558
|
-
ref: { sessionKey: row.session_key, chunkId: row.id, turnId: row.turn_id, seq: row.seq },
|
|
1559
|
-
taskId: row.task_id ?? null,
|
|
1560
|
-
skillId: row.skill_id ?? null,
|
|
1561
|
-
}));
|
|
1562
|
-
return { hits, meta: { total: hits.length, usedMaxResults: maxResults } };
|
|
1563
|
-
}
|
|
1564
|
-
handleSharingTaskShare(req, res) {
|
|
1565
|
-
this.readBody(req, async (body) => {
|
|
1566
|
-
if (!this.ctx)
|
|
1567
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1568
|
-
try {
|
|
1569
|
-
const parsed = JSON.parse(body || "{}");
|
|
1570
|
-
const taskId = String(parsed.taskId || "");
|
|
1571
|
-
const visibility = "public";
|
|
1572
|
-
const groupId = undefined;
|
|
1573
|
-
const task = this.store.getTask(taskId);
|
|
1574
|
-
if (!task)
|
|
1575
|
-
return this.jsonResponse(res, { ok: false, error: "task_not_found" });
|
|
1576
|
-
const chunks = this.store.getChunksByTask(taskId);
|
|
1577
|
-
if (chunks.length === 0)
|
|
1578
|
-
return this.jsonResponse(res, { ok: false, error: "no_chunks" });
|
|
1579
|
-
const hubClient = await this.resolveHubClientAware();
|
|
1580
|
-
const response = await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/tasks/share", {
|
|
1581
|
-
method: "POST",
|
|
1582
|
-
body: JSON.stringify({
|
|
1583
|
-
task: {
|
|
1584
|
-
id: task.id,
|
|
1585
|
-
sourceTaskId: task.id,
|
|
1586
|
-
title: task.title,
|
|
1587
|
-
summary: task.summary,
|
|
1588
|
-
groupId: null,
|
|
1589
|
-
visibility,
|
|
1590
|
-
createdAt: task.startedAt ?? Date.now(),
|
|
1591
|
-
updatedAt: task.updatedAt ?? Date.now(),
|
|
1592
|
-
},
|
|
1593
|
-
chunks: chunks.map((chunk) => ({
|
|
1594
|
-
id: chunk.id,
|
|
1595
|
-
hubTaskId: task.id,
|
|
1596
|
-
sourceTaskId: task.id,
|
|
1597
|
-
sourceChunkId: chunk.id,
|
|
1598
|
-
role: chunk.role,
|
|
1599
|
-
content: chunk.content,
|
|
1600
|
-
summary: chunk.summary,
|
|
1601
|
-
kind: chunk.kind,
|
|
1602
|
-
createdAt: chunk.createdAt,
|
|
1603
|
-
})),
|
|
1604
|
-
}),
|
|
1605
|
-
});
|
|
1606
|
-
const hubUserId = hubClient.userId;
|
|
1607
|
-
if (hubUserId) {
|
|
1608
|
-
this.store.upsertHubTask({
|
|
1609
|
-
id: task.id,
|
|
1610
|
-
sourceTaskId: task.id,
|
|
1611
|
-
sourceUserId: hubUserId,
|
|
1612
|
-
title: task.title,
|
|
1613
|
-
summary: task.summary,
|
|
1614
|
-
groupId: null,
|
|
1615
|
-
visibility,
|
|
1616
|
-
createdAt: task.startedAt ?? Date.now(),
|
|
1617
|
-
updatedAt: task.updatedAt ?? Date.now(),
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
|
-
this.jsonResponse(res, { ok: true, taskId, visibility, response });
|
|
1621
|
-
}
|
|
1622
|
-
catch (err) {
|
|
1623
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1624
|
-
}
|
|
1625
|
-
});
|
|
1626
|
-
}
|
|
1627
|
-
handleSharingTaskUnshare(req, res) {
|
|
1628
|
-
this.readBody(req, async (body) => {
|
|
1629
|
-
if (!this.ctx)
|
|
1630
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1631
|
-
try {
|
|
1632
|
-
const parsed = JSON.parse(body || "{}");
|
|
1633
|
-
const taskId = String(parsed.taskId || "");
|
|
1634
|
-
const task = this.store.getTask(taskId);
|
|
1635
|
-
if (!task)
|
|
1636
|
-
return this.jsonResponse(res, { ok: false, error: "task_not_found" });
|
|
1637
|
-
const hubClient = await this.resolveHubClientAware();
|
|
1638
|
-
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/tasks/unshare", {
|
|
1639
|
-
method: "POST",
|
|
1640
|
-
body: JSON.stringify({ sourceTaskId: task.id }),
|
|
1641
|
-
});
|
|
1642
|
-
const hubUserId = hubClient.userId;
|
|
1643
|
-
if (hubUserId)
|
|
1644
|
-
this.store.deleteHubTaskBySource(hubUserId, task.id);
|
|
1645
|
-
this.jsonResponse(res, { ok: true, taskId });
|
|
1646
|
-
}
|
|
1647
|
-
catch (err) {
|
|
1648
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1649
|
-
}
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
handleSharingMemoryShare(req, res) {
|
|
1653
|
-
this.readBody(req, async (body) => {
|
|
1654
|
-
if (!this.ctx)
|
|
1655
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1656
|
-
try {
|
|
1657
|
-
const parsed = JSON.parse(body || "{}");
|
|
1658
|
-
const chunkId = String(parsed.chunkId || "");
|
|
1659
|
-
const visibility = "public";
|
|
1660
|
-
const groupId = undefined;
|
|
1661
|
-
const db = this.store.db;
|
|
1662
|
-
const chunk = db.prepare("SELECT * FROM chunks WHERE id = ?").get(chunkId);
|
|
1663
|
-
if (!chunk)
|
|
1664
|
-
return this.jsonResponse(res, { ok: false, error: "memory_not_found" });
|
|
1665
|
-
const hubClient = await this.resolveHubClientAware();
|
|
1666
|
-
const response = await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/memories/share", {
|
|
1667
|
-
method: "POST",
|
|
1668
|
-
body: JSON.stringify({
|
|
1669
|
-
memory: {
|
|
1670
|
-
sourceChunkId: chunk.id,
|
|
1671
|
-
role: chunk.role,
|
|
1672
|
-
content: chunk.content,
|
|
1673
|
-
summary: chunk.summary,
|
|
1674
|
-
kind: chunk.kind,
|
|
1675
|
-
groupId: null,
|
|
1676
|
-
visibility,
|
|
1677
|
-
},
|
|
1678
|
-
}),
|
|
1679
|
-
});
|
|
1680
|
-
const hubUserId = hubClient.userId;
|
|
1681
|
-
if (hubUserId) {
|
|
1682
|
-
const now = Date.now();
|
|
1683
|
-
const existing = this.store.getHubMemoryBySource(hubUserId, chunk.id);
|
|
1684
|
-
this.store.upsertHubMemory({
|
|
1685
|
-
id: response?.memoryId ?? existing?.id ?? node_crypto_1.default.randomUUID(),
|
|
1686
|
-
sourceChunkId: chunk.id,
|
|
1687
|
-
sourceUserId: hubUserId,
|
|
1688
|
-
role: chunk.role,
|
|
1689
|
-
content: chunk.content,
|
|
1690
|
-
summary: chunk.summary ?? "",
|
|
1691
|
-
kind: chunk.kind,
|
|
1692
|
-
groupId: null,
|
|
1693
|
-
visibility,
|
|
1694
|
-
createdAt: existing?.createdAt ?? now,
|
|
1695
|
-
updatedAt: now,
|
|
1696
|
-
});
|
|
1697
|
-
}
|
|
1698
|
-
this.jsonResponse(res, { ok: true, chunkId, visibility, response });
|
|
1699
|
-
}
|
|
1700
|
-
catch (err) {
|
|
1701
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1702
|
-
}
|
|
1703
|
-
});
|
|
1704
|
-
}
|
|
1705
|
-
handleSharingMemoryUnshare(req, res) {
|
|
1706
|
-
this.readBody(req, async (body) => {
|
|
1707
|
-
if (!this.ctx)
|
|
1708
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1709
|
-
try {
|
|
1710
|
-
const parsed = JSON.parse(body || "{}");
|
|
1711
|
-
const chunkId = String(parsed.chunkId || "");
|
|
1712
|
-
const hubClient = await this.resolveHubClientAware();
|
|
1713
|
-
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/memories/unshare", {
|
|
1714
|
-
method: "POST",
|
|
1715
|
-
body: JSON.stringify({ sourceChunkId: chunkId }),
|
|
1716
|
-
});
|
|
1717
|
-
const hubUserId = hubClient.userId;
|
|
1718
|
-
if (hubUserId)
|
|
1719
|
-
this.store.deleteHubMemoryBySource(hubUserId, chunkId);
|
|
1720
|
-
this.jsonResponse(res, { ok: true, chunkId });
|
|
1721
|
-
}
|
|
1722
|
-
catch (err) {
|
|
1723
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1724
|
-
}
|
|
1725
|
-
});
|
|
1726
|
-
}
|
|
1727
|
-
handleSharingSkillPull(req, res) {
|
|
1728
|
-
this.readBody(req, async (body) => {
|
|
1729
|
-
if (!this.ctx)
|
|
1730
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1731
|
-
try {
|
|
1732
|
-
const parsed = JSON.parse(body || "{}");
|
|
1733
|
-
const skillId = String(parsed.skillId || "");
|
|
1734
|
-
const payload = await (0, skill_sync_1.fetchHubSkillBundle)(this.store, this.ctx, { skillId });
|
|
1735
|
-
const restored = (0, skill_sync_1.restoreSkillBundleFromHub)(this.store, this.ctx, payload);
|
|
1736
|
-
this.jsonResponse(res, { ok: true, pulled: true, hubSkillId: skillId, ...restored });
|
|
1737
|
-
}
|
|
1738
|
-
catch (err) {
|
|
1739
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1740
|
-
}
|
|
1741
|
-
});
|
|
1742
|
-
}
|
|
1743
|
-
handleSharingSkillShare(req, res) {
|
|
1744
|
-
this.readBody(req, async (body) => {
|
|
1745
|
-
if (!this.ctx)
|
|
1746
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1747
|
-
try {
|
|
1748
|
-
const parsed = JSON.parse(body || "{}");
|
|
1749
|
-
const skillId = String(parsed.skillId || "");
|
|
1750
|
-
const visibility = "public";
|
|
1751
|
-
const groupId = null;
|
|
1752
|
-
const skill = this.store.getSkill(skillId);
|
|
1753
|
-
if (!skill)
|
|
1754
|
-
return this.jsonResponse(res, { ok: false, error: "skill_not_found" });
|
|
1755
|
-
const bundle = (0, skill_sync_1.buildSkillBundleForHub)(this.store, skillId);
|
|
1756
|
-
const hubClient = await this.resolveHubClientAware();
|
|
1757
|
-
const response = await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/skills/publish", {
|
|
1758
|
-
method: "POST",
|
|
1759
|
-
body: JSON.stringify({
|
|
1760
|
-
visibility,
|
|
1761
|
-
groupId: null,
|
|
1762
|
-
metadata: bundle.metadata,
|
|
1763
|
-
bundle: bundle.bundle,
|
|
1764
|
-
}),
|
|
1765
|
-
});
|
|
1766
|
-
const hubUserId = hubClient.userId;
|
|
1767
|
-
if (hubUserId) {
|
|
1768
|
-
const existing = this.store.getHubSkillBySource(hubUserId, skillId);
|
|
1769
|
-
this.store.upsertHubSkill({
|
|
1770
|
-
id: response?.skillId ?? existing?.id ?? node_crypto_1.default.randomUUID(),
|
|
1771
|
-
sourceSkillId: skillId,
|
|
1772
|
-
sourceUserId: hubUserId,
|
|
1773
|
-
name: skill.name,
|
|
1774
|
-
description: skill.description,
|
|
1775
|
-
version: skill.version,
|
|
1776
|
-
groupId: null,
|
|
1777
|
-
visibility,
|
|
1778
|
-
bundle: JSON.stringify(bundle.bundle),
|
|
1779
|
-
qualityScore: skill.qualityScore,
|
|
1780
|
-
createdAt: existing?.createdAt ?? Date.now(),
|
|
1781
|
-
updatedAt: Date.now(),
|
|
1782
|
-
});
|
|
1783
|
-
}
|
|
1784
|
-
this.jsonResponse(res, { ok: true, skillId, visibility, response });
|
|
1785
|
-
}
|
|
1786
|
-
catch (err) {
|
|
1787
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1788
|
-
}
|
|
1789
|
-
});
|
|
1790
|
-
}
|
|
1791
|
-
handleSharingSkillUnshare(req, res) {
|
|
1792
|
-
this.readBody(req, async (body) => {
|
|
1793
|
-
if (!this.ctx)
|
|
1794
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
1795
|
-
try {
|
|
1796
|
-
const parsed = JSON.parse(body || "{}");
|
|
1797
|
-
const skillId = String(parsed.skillId || "");
|
|
1798
|
-
const skill = this.store.getSkill(skillId);
|
|
1799
|
-
if (!skill)
|
|
1800
|
-
return this.jsonResponse(res, { ok: false, error: "skill_not_found" });
|
|
1801
|
-
const hubClient = await this.resolveHubClientAware();
|
|
1802
|
-
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/skills/unpublish", {
|
|
1803
|
-
method: "POST",
|
|
1804
|
-
body: JSON.stringify({ sourceSkillId: skill.id }),
|
|
1805
|
-
});
|
|
1806
|
-
const hubUserId = hubClient.userId;
|
|
1807
|
-
if (hubUserId)
|
|
1808
|
-
this.store.deleteHubSkillBySource(hubUserId, skill.id);
|
|
1809
|
-
this.jsonResponse(res, { ok: true, skillId });
|
|
1810
|
-
}
|
|
1811
|
-
catch (err) {
|
|
1812
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1813
|
-
}
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
resolveHubConnection() {
|
|
1817
|
-
if (!this.ctx)
|
|
1818
|
-
return null;
|
|
1819
|
-
// Hub 模式:连接自己,用 bootstrap admin token
|
|
1820
|
-
const sharing = this.ctx.config.sharing;
|
|
1821
|
-
if (sharing?.role === "hub") {
|
|
1822
|
-
const hubPort = sharing.hub?.port ?? 18800;
|
|
1823
|
-
const hubUrl = `http://127.0.0.1:${hubPort}`;
|
|
1824
|
-
try {
|
|
1825
|
-
const authPath = node_path_1.default.join(this.dataDir, "hub-auth.json");
|
|
1826
|
-
const authData = JSON.parse(node_fs_1.default.readFileSync(authPath, "utf8"));
|
|
1827
|
-
const adminToken = authData?.bootstrapAdminToken;
|
|
1828
|
-
if (adminToken)
|
|
1829
|
-
return { hubUrl, userToken: adminToken };
|
|
1830
|
-
}
|
|
1831
|
-
catch {
|
|
1832
|
-
// hub-auth.json 不存在或读取失败,fall through
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
// Client 模式:用配置的 hubAddress + userToken
|
|
1836
|
-
const conn = this.store.getClientHubConnection();
|
|
1837
|
-
const hubUrl = conn?.hubUrl || this.ctx.config.sharing?.client?.hubAddress || "";
|
|
1838
|
-
const userToken = conn?.userToken || this.ctx.config.sharing?.client?.userToken || "";
|
|
1839
|
-
if (!hubUrl || !userToken)
|
|
1840
|
-
return null;
|
|
1841
|
-
return { hubUrl: (0, hub_1.normalizeHubUrl)(hubUrl), userToken };
|
|
1842
|
-
}
|
|
1843
|
-
/** resolveHubClient 的 viewer 版本:hub 模式下使用 bootstrap admin 身份 */
|
|
1844
|
-
async resolveHubClientAware() {
|
|
1845
|
-
if (!this.ctx)
|
|
1846
|
-
throw new Error("sharing_unavailable");
|
|
1847
|
-
const sharing = this.ctx.config.sharing;
|
|
1848
|
-
if (sharing?.role === "hub") {
|
|
1849
|
-
const hub = this.resolveHubConnection();
|
|
1850
|
-
if (hub) {
|
|
1851
|
-
const me = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/me", { method: "GET" });
|
|
1852
|
-
return {
|
|
1853
|
-
hubUrl: hub.hubUrl,
|
|
1854
|
-
userToken: hub.userToken,
|
|
1855
|
-
userId: String(me.id),
|
|
1856
|
-
username: String(me.username ?? "hub-admin"),
|
|
1857
|
-
role: String(me.role ?? "admin"),
|
|
1858
|
-
};
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
return (0, hub_1.resolveHubClient)(this.store, this.ctx);
|
|
1862
|
-
}
|
|
1863
|
-
extractGroupId(path) {
|
|
1864
|
-
const m = path.match(/\/api\/sharing\/groups\/([^/]+)/);
|
|
1865
|
-
return m ? decodeURIComponent(m[1]) : "";
|
|
1866
|
-
}
|
|
1867
|
-
async serveSharingGroups(res) {
|
|
1868
|
-
const hub = this.resolveHubConnection();
|
|
1869
|
-
if (!hub)
|
|
1870
|
-
return this.jsonResponse(res, { groups: [], error: "not_configured" });
|
|
1871
|
-
try {
|
|
1872
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/groups", { method: "GET" });
|
|
1873
|
-
this.jsonResponse(res, { groups: Array.isArray(data?.groups) ? data.groups : [] });
|
|
1874
|
-
}
|
|
1875
|
-
catch (err) {
|
|
1876
|
-
this.jsonResponse(res, { groups: [], error: String(err) });
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
handleSharingGroupCreate(req, res) {
|
|
1880
|
-
this.readBody(req, async (body) => {
|
|
1881
|
-
const hub = this.resolveHubConnection();
|
|
1882
|
-
if (!hub)
|
|
1883
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1884
|
-
try {
|
|
1885
|
-
const parsed = JSON.parse(body || "{}");
|
|
1886
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/groups", {
|
|
1887
|
-
method: "POST",
|
|
1888
|
-
body: JSON.stringify({ name: parsed.name, description: parsed.description }),
|
|
1889
|
-
});
|
|
1890
|
-
this.jsonResponse(res, { ok: true, ...data });
|
|
1891
|
-
}
|
|
1892
|
-
catch (err) {
|
|
1893
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1894
|
-
}
|
|
1895
|
-
});
|
|
1896
|
-
}
|
|
1897
|
-
handleSharingGroupUpdate(req, res, p) {
|
|
1898
|
-
this.readBody(req, async (body) => {
|
|
1899
|
-
const hub = this.resolveHubConnection();
|
|
1900
|
-
if (!hub)
|
|
1901
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1902
|
-
const groupId = this.extractGroupId(p);
|
|
1903
|
-
try {
|
|
1904
|
-
const parsed = JSON.parse(body || "{}");
|
|
1905
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/groups/${encodeURIComponent(groupId)}`, {
|
|
1906
|
-
method: "PUT",
|
|
1907
|
-
body: JSON.stringify({ name: parsed.name, description: parsed.description }),
|
|
1908
|
-
});
|
|
1909
|
-
this.jsonResponse(res, { ok: true });
|
|
1910
|
-
}
|
|
1911
|
-
catch (err) {
|
|
1912
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1913
|
-
}
|
|
1914
|
-
});
|
|
1915
|
-
}
|
|
1916
|
-
async handleSharingGroupDelete(res, p) {
|
|
1917
|
-
const hub = this.resolveHubConnection();
|
|
1918
|
-
if (!hub)
|
|
1919
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1920
|
-
const groupId = this.extractGroupId(p);
|
|
1921
|
-
try {
|
|
1922
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/groups/${encodeURIComponent(groupId)}`, { method: "DELETE" });
|
|
1923
|
-
this.jsonResponse(res, { ok: true });
|
|
1924
|
-
}
|
|
1925
|
-
catch (err) {
|
|
1926
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
async serveSharingGroupMembers(res, p) {
|
|
1930
|
-
const hub = this.resolveHubConnection();
|
|
1931
|
-
if (!hub)
|
|
1932
|
-
return this.jsonResponse(res, { members: [], error: "not_configured" });
|
|
1933
|
-
const groupId = this.extractGroupId(p);
|
|
1934
|
-
try {
|
|
1935
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/groups/${encodeURIComponent(groupId)}`, { method: "GET" });
|
|
1936
|
-
this.jsonResponse(res, { members: Array.isArray(data?.members) ? data.members : [] });
|
|
1937
|
-
}
|
|
1938
|
-
catch (err) {
|
|
1939
|
-
this.jsonResponse(res, { members: [], error: String(err) });
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
handleSharingGroupAddMember(req, res, p) {
|
|
1943
|
-
this.readBody(req, async (body) => {
|
|
1944
|
-
const hub = this.resolveHubConnection();
|
|
1945
|
-
if (!hub)
|
|
1946
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1947
|
-
const groupId = this.extractGroupId(p);
|
|
1948
|
-
try {
|
|
1949
|
-
const parsed = JSON.parse(body || "{}");
|
|
1950
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/groups/${encodeURIComponent(groupId)}/members`, {
|
|
1951
|
-
method: "POST",
|
|
1952
|
-
body: JSON.stringify({ userId: parsed.userId }),
|
|
1953
|
-
});
|
|
1954
|
-
this.jsonResponse(res, { ok: true });
|
|
1955
|
-
}
|
|
1956
|
-
catch (err) {
|
|
1957
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1958
|
-
}
|
|
1959
|
-
});
|
|
1960
|
-
}
|
|
1961
|
-
handleSharingGroupRemoveMember(req, res, p) {
|
|
1962
|
-
this.readBody(req, async (body) => {
|
|
1963
|
-
const hub = this.resolveHubConnection();
|
|
1964
|
-
if (!hub)
|
|
1965
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
1966
|
-
const groupId = this.extractGroupId(p);
|
|
1967
|
-
try {
|
|
1968
|
-
const parsed = JSON.parse(body || "{}");
|
|
1969
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/groups/${encodeURIComponent(groupId)}/members`, {
|
|
1970
|
-
method: "DELETE",
|
|
1971
|
-
body: JSON.stringify({ userId: parsed.userId }),
|
|
1972
|
-
});
|
|
1973
|
-
this.jsonResponse(res, { ok: true });
|
|
1974
|
-
}
|
|
1975
|
-
catch (err) {
|
|
1976
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
1977
|
-
}
|
|
1978
|
-
});
|
|
1979
|
-
}
|
|
1980
|
-
async serveSharingUsers(res) {
|
|
1981
|
-
const hub = this.resolveHubConnection();
|
|
1982
|
-
if (!hub)
|
|
1983
|
-
return this.jsonResponse(res, { users: [], error: "not_configured" });
|
|
1984
|
-
try {
|
|
1985
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/users", { method: "GET" });
|
|
1986
|
-
this.jsonResponse(res, { users: Array.isArray(data?.users) ? data.users : [] });
|
|
1987
|
-
}
|
|
1988
|
-
catch (err) {
|
|
1989
|
-
this.jsonResponse(res, { users: [], error: String(err) });
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
// ─── Admin management endpoints (Hub-side data) ───
|
|
1993
|
-
async serveAdminSharedTasks(res) {
|
|
1994
|
-
const hub = this.resolveHubConnection();
|
|
1995
|
-
if (!hub)
|
|
1996
|
-
return this.jsonResponse(res, { tasks: [], error: "not_configured" });
|
|
1997
|
-
try {
|
|
1998
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/shared-tasks", { method: "GET" });
|
|
1999
|
-
this.jsonResponse(res, { tasks: Array.isArray(data?.tasks) ? data.tasks : [] });
|
|
2000
|
-
}
|
|
2001
|
-
catch (err) {
|
|
2002
|
-
this.jsonResponse(res, { tasks: [], error: String(err) });
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
async handleAdminDeleteTask(res, p) {
|
|
2006
|
-
const hub = this.resolveHubConnection();
|
|
2007
|
-
if (!hub)
|
|
2008
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
2009
|
-
const taskId = decodeURIComponent(p.replace("/api/admin/shared-tasks/", ""));
|
|
2010
|
-
try {
|
|
2011
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/admin/shared-tasks/${encodeURIComponent(taskId)}`, { method: "DELETE" });
|
|
2012
|
-
this.jsonResponse(res, { ok: true });
|
|
2013
|
-
}
|
|
2014
|
-
catch (err) {
|
|
2015
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
async serveAdminSharedSkills(res) {
|
|
2019
|
-
const hub = this.resolveHubConnection();
|
|
2020
|
-
if (!hub)
|
|
2021
|
-
return this.jsonResponse(res, { skills: [], error: "not_configured" });
|
|
2022
|
-
try {
|
|
2023
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/shared-skills", { method: "GET" });
|
|
2024
|
-
this.jsonResponse(res, { skills: Array.isArray(data?.skills) ? data.skills : [] });
|
|
2025
|
-
}
|
|
2026
|
-
catch (err) {
|
|
2027
|
-
this.jsonResponse(res, { skills: [], error: String(err) });
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
async handleAdminDeleteSkill(res, p) {
|
|
2031
|
-
const hub = this.resolveHubConnection();
|
|
2032
|
-
if (!hub)
|
|
2033
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
2034
|
-
const skillId = decodeURIComponent(p.replace("/api/admin/shared-skills/", ""));
|
|
2035
|
-
try {
|
|
2036
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/admin/shared-skills/${encodeURIComponent(skillId)}`, { method: "DELETE" });
|
|
2037
|
-
this.jsonResponse(res, { ok: true });
|
|
2038
|
-
}
|
|
2039
|
-
catch (err) {
|
|
2040
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
async serveAdminSharedMemories(res) {
|
|
2044
|
-
const hub = this.resolveHubConnection();
|
|
2045
|
-
if (!hub)
|
|
2046
|
-
return this.jsonResponse(res, { memories: [], error: "not_configured" });
|
|
2047
|
-
try {
|
|
2048
|
-
const data = await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, "/api/v1/hub/admin/shared-memories", { method: "GET" });
|
|
2049
|
-
this.jsonResponse(res, { memories: Array.isArray(data?.memories) ? data.memories : [] });
|
|
2050
|
-
}
|
|
2051
|
-
catch (err) {
|
|
2052
|
-
this.jsonResponse(res, { memories: [], error: String(err) });
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
async handleAdminDeleteMemory(res, p) {
|
|
2056
|
-
const hub = this.resolveHubConnection();
|
|
2057
|
-
if (!hub)
|
|
2058
|
-
return this.jsonResponse(res, { ok: false, error: "not_configured" });
|
|
2059
|
-
const memoryId = decodeURIComponent(p.replace("/api/admin/shared-memories/", ""));
|
|
2060
|
-
try {
|
|
2061
|
-
await (0, hub_1.hubRequestJson)(hub.hubUrl, hub.userToken, `/api/v1/hub/admin/shared-memories/${encodeURIComponent(memoryId)}`, { method: "DELETE" });
|
|
2062
|
-
this.jsonResponse(res, { ok: true });
|
|
2063
|
-
}
|
|
2064
|
-
catch (err) {
|
|
2065
|
-
this.jsonResponse(res, { ok: false, error: String(err) });
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
getLocalIPs() {
|
|
2069
|
-
const nets = node_os_1.default.networkInterfaces();
|
|
2070
|
-
const ips = [];
|
|
2071
|
-
for (const name of Object.keys(nets)) {
|
|
2072
|
-
for (const net of nets[name] ?? []) {
|
|
2073
|
-
if (net.family === "IPv4" && !net.internal) {
|
|
2074
|
-
ips.push(net.address);
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
return ips;
|
|
2079
|
-
}
|
|
2080
|
-
serveLocalIPs(res) {
|
|
2081
|
-
const ips = this.getLocalIPs();
|
|
2082
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2083
|
-
res.end(JSON.stringify({ ips }));
|
|
1072
|
+
const ocHome = process.env.OPENCLAW_STATE_DIR || node_path_1.default.join(home, ".openclaw");
|
|
1073
|
+
return node_path_1.default.join(ocHome, "openclaw.json");
|
|
2084
1074
|
}
|
|
2085
1075
|
serveConfig(res) {
|
|
2086
1076
|
try {
|
|
@@ -2102,10 +1092,7 @@ class ViewerServer {
|
|
|
2102
1092
|
?? entries["memos-lite-openclaw-plugin"]
|
|
2103
1093
|
?? entries["memos-lite"]
|
|
2104
1094
|
?? {};
|
|
2105
|
-
if (pluginEntry.viewerPort
|
|
2106
|
-
result.viewerPort = pluginEntry.viewerPort;
|
|
2107
|
-
}
|
|
2108
|
-
else if (topEntry.viewerPort) {
|
|
1095
|
+
if (pluginEntry.viewerPort == null && topEntry.viewerPort) {
|
|
2109
1096
|
result.viewerPort = topEntry.viewerPort;
|
|
2110
1097
|
}
|
|
2111
1098
|
this.jsonResponse(res, result);
|
|
@@ -2151,37 +1138,6 @@ class ViewerServer {
|
|
|
2151
1138
|
config.viewerPort = newCfg.viewerPort;
|
|
2152
1139
|
if (newCfg.telemetry !== undefined)
|
|
2153
1140
|
config.telemetry = newCfg.telemetry;
|
|
2154
|
-
if (newCfg.sharing !== undefined) {
|
|
2155
|
-
const existing = config.sharing || {};
|
|
2156
|
-
const merged = { ...existing, ...newCfg.sharing };
|
|
2157
|
-
if (newCfg.sharing.capabilities && existing.capabilities) {
|
|
2158
|
-
merged.capabilities = { ...existing.capabilities, ...newCfg.sharing.capabilities };
|
|
2159
|
-
}
|
|
2160
|
-
if (merged.role === "client" && merged.client) {
|
|
2161
|
-
const clientCfg = merged.client;
|
|
2162
|
-
const addr = String(clientCfg.hubAddress || "");
|
|
2163
|
-
if (addr) {
|
|
2164
|
-
const localIPs = this.getLocalIPs();
|
|
2165
|
-
localIPs.push("127.0.0.1", "localhost", "0.0.0.0");
|
|
2166
|
-
try {
|
|
2167
|
-
const u = new URL(addr.startsWith("http") ? addr : `http://${addr}`);
|
|
2168
|
-
if (localIPs.includes(u.hostname)) {
|
|
2169
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2170
|
-
res.end(JSON.stringify({ error: "cannot_join_self" }));
|
|
2171
|
-
return;
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
catch { }
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
if (merged.role === "hub") {
|
|
2178
|
-
merged.client = { hubAddress: "", userToken: "", teamToken: "" };
|
|
2179
|
-
}
|
|
2180
|
-
else if (merged.role === "client") {
|
|
2181
|
-
merged.hub = { port: 18800, teamName: "", teamToken: "" };
|
|
2182
|
-
}
|
|
2183
|
-
config.sharing = merged;
|
|
2184
|
-
}
|
|
2185
1141
|
node_fs_1.default.mkdirSync(node_path_1.default.dirname(cfgPath), { recursive: true });
|
|
2186
1142
|
node_fs_1.default.writeFileSync(cfgPath, JSON.stringify(raw, null, 2), "utf-8");
|
|
2187
1143
|
this.log.info("Plugin config updated via Viewer");
|
|
@@ -2194,99 +1150,6 @@ class ViewerServer {
|
|
|
2194
1150
|
}
|
|
2195
1151
|
});
|
|
2196
1152
|
}
|
|
2197
|
-
handleUpdateUsername(req, res) {
|
|
2198
|
-
this.readBody(req, async (body) => {
|
|
2199
|
-
if (!this.ctx)
|
|
2200
|
-
return this.jsonResponse(res, { error: "sharing_unavailable" });
|
|
2201
|
-
try {
|
|
2202
|
-
const { username } = JSON.parse(body || "{}");
|
|
2203
|
-
if (!username || typeof username !== "string" || username.trim().length < 2 || username.trim().length > 32) {
|
|
2204
|
-
return this.jsonResponse(res, { error: "invalid_username" }, 400);
|
|
2205
|
-
}
|
|
2206
|
-
const trimmed = username.trim();
|
|
2207
|
-
const hubClient = await this.resolveHubClientAware();
|
|
2208
|
-
const result = await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/me/update-profile", {
|
|
2209
|
-
method: "POST",
|
|
2210
|
-
body: JSON.stringify({ username: trimmed }),
|
|
2211
|
-
});
|
|
2212
|
-
if (result.ok && result.userToken) {
|
|
2213
|
-
const sharing = this.ctx.config.sharing;
|
|
2214
|
-
if (sharing?.role === "hub") {
|
|
2215
|
-
try {
|
|
2216
|
-
const authPath = node_path_1.default.join(this.dataDir, "hub-auth.json");
|
|
2217
|
-
const authData = JSON.parse(node_fs_1.default.readFileSync(authPath, "utf8"));
|
|
2218
|
-
authData.bootstrapAdminToken = result.userToken;
|
|
2219
|
-
node_fs_1.default.writeFileSync(authPath, JSON.stringify(authData, null, 2), "utf-8");
|
|
2220
|
-
this.log.info("hub-auth.json updated with new admin token after username change");
|
|
2221
|
-
}
|
|
2222
|
-
catch (e) {
|
|
2223
|
-
this.log.warn(`Failed to update hub-auth.json: ${e}`);
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
else {
|
|
2227
|
-
const persisted = this.store.getClientHubConnection();
|
|
2228
|
-
if (persisted) {
|
|
2229
|
-
this.store.setClientHubConnection({
|
|
2230
|
-
...persisted,
|
|
2231
|
-
username: result.username,
|
|
2232
|
-
userToken: result.userToken,
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
this.jsonResponse(res, result);
|
|
2238
|
-
}
|
|
2239
|
-
catch (err) {
|
|
2240
|
-
const msg = String(err?.message || err);
|
|
2241
|
-
if (msg.includes("409") || msg.includes("username_taken")) {
|
|
2242
|
-
return this.jsonResponse(res, { error: "username_taken" }, 409);
|
|
2243
|
-
}
|
|
2244
|
-
this.jsonResponse(res, { error: msg }, 500);
|
|
2245
|
-
}
|
|
2246
|
-
});
|
|
2247
|
-
}
|
|
2248
|
-
handleTestHubConnection(req, res) {
|
|
2249
|
-
this.readBody(req, async (body) => {
|
|
2250
|
-
try {
|
|
2251
|
-
const { hubUrl } = JSON.parse(body);
|
|
2252
|
-
if (!hubUrl) {
|
|
2253
|
-
this.jsonResponse(res, { ok: false, error: "hubUrl is required" });
|
|
2254
|
-
return;
|
|
2255
|
-
}
|
|
2256
|
-
try {
|
|
2257
|
-
const localIPs = this.getLocalIPs();
|
|
2258
|
-
localIPs.push("127.0.0.1", "localhost", "0.0.0.0");
|
|
2259
|
-
const parsed = new URL(hubUrl.startsWith("http") ? hubUrl : `http://${hubUrl}`);
|
|
2260
|
-
if (localIPs.includes(parsed.hostname)) {
|
|
2261
|
-
this.jsonResponse(res, { ok: false, error: "cannot_join_self" });
|
|
2262
|
-
return;
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
catch { }
|
|
2266
|
-
const url = hubUrl.replace(/\/+$/, "") + "/api/v1/hub/info";
|
|
2267
|
-
const ctrl = new AbortController();
|
|
2268
|
-
const timeout = setTimeout(() => ctrl.abort(), 8000);
|
|
2269
|
-
try {
|
|
2270
|
-
const r = await fetch(url, { signal: ctrl.signal });
|
|
2271
|
-
clearTimeout(timeout);
|
|
2272
|
-
if (!r.ok) {
|
|
2273
|
-
this.jsonResponse(res, { ok: false, error: `HTTP ${r.status}` });
|
|
2274
|
-
return;
|
|
2275
|
-
}
|
|
2276
|
-
const info = await r.json();
|
|
2277
|
-
this.jsonResponse(res, { ok: true, teamName: info.teamName || "", apiVersion: info.apiVersion || "" });
|
|
2278
|
-
}
|
|
2279
|
-
catch (e) {
|
|
2280
|
-
clearTimeout(timeout);
|
|
2281
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
2282
|
-
this.jsonResponse(res, { ok: false, error: msg.includes("abort") ? "Connection timeout (8s)" : msg });
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2285
|
-
catch (e) {
|
|
2286
|
-
this.jsonResponse(res, { ok: false, error: String(e) });
|
|
2287
|
-
}
|
|
2288
|
-
});
|
|
2289
|
-
}
|
|
2290
1153
|
handleTestModel(req, res) {
|
|
2291
1154
|
this.readBody(req, async (body) => {
|
|
2292
1155
|
try {
|
|
@@ -2668,7 +1531,7 @@ class ViewerServer {
|
|
|
2668
1531
|
// ─── Migration: scan OpenClaw built-in memory ───
|
|
2669
1532
|
getOpenClawHome() {
|
|
2670
1533
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
2671
|
-
return node_path_1.default.join(home, ".openclaw");
|
|
1534
|
+
return process.env.OPENCLAW_STATE_DIR || node_path_1.default.join(home, ".openclaw");
|
|
2672
1535
|
}
|
|
2673
1536
|
handleCleanupPolluted(res) {
|
|
2674
1537
|
try {
|
|
@@ -3598,8 +2461,8 @@ class ViewerServer {
|
|
|
3598
2461
|
req.on("data", (chunk) => { body += chunk.toString(); });
|
|
3599
2462
|
req.on("end", () => cb(body));
|
|
3600
2463
|
}
|
|
3601
|
-
jsonResponse(res, data
|
|
3602
|
-
res.writeHead(
|
|
2464
|
+
jsonResponse(res, data) {
|
|
2465
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
3603
2466
|
res.end(JSON.stringify(data));
|
|
3604
2467
|
}
|
|
3605
2468
|
}
|